源码探究-openfeign

本文详细介绍了OpenFeign如何在Spring容器中加载远程接口实例,涉及`@EnableFeignClients`注解的使用,以及Feign通过JDK代理实现的远程调用原理,重点讲解了`FeignClientFactoryBean`、`FeignInvocationHandler`和`SynchronousMethodHandler`的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文分两个部分:

1、项目启动,openfeign如何加载远程接口实例化并且注入到容器中

2、远程调用实现原理

1、openfeign加载

1.1、在启动类加入开启feign注解
@EnableFeignClients
1.2、远程调用接口

1.3、在使用的地方注入

1.4、openfeign工作原理

我们知道openfeign是一个rpc远程调用框架,其本质上还是http请求访问。

openFeign是通过jdk代理来实现这个操作的,下面我们将通过源码一步步探寻它的运作原理。

我们的目的很简单,因为我们在使用的时候是直接进行注入的,我们只需要知道每一个接口是如何被注入到Spring容器的,找到代理所做的事情即可。

1.5、@EnableFeignClients

我们先看最开始的注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {


	String[] value() default {};

	
	String[] basePackages() default {};

	
	Class<?>[] basePackageClasses() default {};

	
	Class<?>[] defaultConfiguration() default {};

	
	Class<?>[] clients() default {};

}

发现有个@Import(FeignClientsRegistrar.class) ,这其实就是将FeignClientsRegistrar类注入,启动类运行会先引入此类。

1.6 FeignClientsRegistrar
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar 并重写了里面的方法registerBeanDefinitions会自动调用这个方法进行初始化。所以我们在看看重写的registerBeanDefinitions做了什么。

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    //注册默认配置
    this.registerDefaultConfiguration(metadata, registry);
    //注册feign客户端
    this.registerFeignClients(metadata, registry);
}
1.7 registerFeignClients

此方法扫描使用了@FeignClient注解的接口并进行注入

这里有两种方式,如果你使用了@EnableFeignClients 的 clients那它就直接去获取对应的class,不通过类路径自动扫描

@EnableFeignClients(clients = {SchedualServiceHi.class, SchedualServiceHi2.class})
    public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 获取@EnableFeignClients注解里面的参数
        Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
        Object basePackages;
        // 如果使用了clients 就根据class加载
        if (clients != null && clients.length != 0) {
            ((Set)basePackages).add(ClassUtils.getPackageName(clazz));
        } else {
            // 否则使用类路径加载
            basePackages = this.getBasePackages(metadata);
        }
        // 迭代全部的接口
        Iterator var17 = ((Set)basePackages).iterator();
        while(var17.hasNext()) {
            String basePackage = (String)var17.next();
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            Iterator var21 = candidateComponents.iterator();
            while(var21.hasNext()) {
                BeanDefinition candidateComponent = (BeanDefinition)var21.next();
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // 进行下一步的注册
                    this.registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }
1.8 registerFeignClient
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
			Map<String, Object> attributes) {
    //...
	BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
    //...
}

可以看到registerFeignClient方法里面生成了一个BeanDefinitionBuilder,而入参是FeignClientFactoryBean

1.9 FeignClientFactoryBean
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {}

可以看到它是实现了FactoryBean 里面有一个getObject是获取bean实例的,FeignClientFactoryBean 重写了这个 getObject

public Object getObject() throws Exception {
    return this.getTarget();
}

<T> T getTarget() {
    FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
    Builder builder = this.feign(context);
    // url 为 null 、""、" "  StringUtils.hasText 就返回false
    if (!StringUtils.hasText(this.url)) {
        // ......
        
    } else {
        // ......
        Targeter targeter = (Targeter)this.get(context, Targeter.class);
        return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
    }
}

Targeter 是一个接口,它有两个实现类,我们来看默认的实现类

这个FactoryBean接口,可以理解成是Spring的一个钩子,它的getObject方法,是在初始化bean的时候会去调用。

target 最终调用的是 Feign.class 方法

public <T> T target(Target<T> target) {
            return this.build().newInstance(target);
}

public Feign build() {
            Client client = (Client)Capability.enrich(this.client, this.capabilities);
            Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);
            List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {
                return (RequestInterceptor)Capability.enrich(ri, this.capabilities);
            }).collect(Collectors.toList());
            Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);
            Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);
            Request.Options options = (Request.Options)Capability.enrich(this.options, this.capabilities);
            Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);
            Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);
            InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
            QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
            SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
            ReflectiveFeign.ParseHandlersByName handlersByName = new ReflectiveFeign.ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
            return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

build()返回一个ReflectiveFeign最后创建一个新实例

public <T> T newInstance(Target<T> target) {
        Map<String, InvocationHandlerFactory.MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
        Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = new LinkedHashMap();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
        Method[] var5 = target.type().getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            if (method.getDeclaringClass() != Object.class) {
                if (Util.isDefault(method)) {
                    DefaultMethodHandler handler = new DefaultMethodHandler(method);
                    defaultMethodHandlers.add(handler);
                    methodToHandler.put(method, handler);
                } else {
                    methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
                }
            }
        }

        InvocationHandler handler = this.factory.create(target, methodToHandler);
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        Iterator var12 = defaultMethodHandlers.iterator();

        while(var12.hasNext()) {
            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }

        return proxy;
    }

newInstance方法里面有一个JDK代理,所以openfeign其实是基于JDK代理来实现的。JDK代理是基于接口代理的,所以主要还是我们的InvocationHandler,它代表了我们代理对象最终指向的内容,所以下面我们就要来看看这个handler是如何产生的。

 1.10 this.targetToHandlersByName.apply(target);
public Map<String, InvocationHandlerFactory.MethodHandler> apply(Target target) {
            List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(target.type());
            Map<String, InvocationHandlerFactory.MethodHandler> result = new LinkedHashMap();
            Iterator var4 = metadata.iterator();

            while(var4.hasNext()) {
                MethodMetadata md = (MethodMetadata)var4.next();
                Object buildTemplate;
                if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
                    buildTemplate = new BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
                } else if (md.bodyIndex() == null && !md.alwaysEncodeBody()) {
                    buildTemplate = new BuildTemplateByResolvingArgs(md, this.queryMapEncoder, target);
                } else {
                    buildTemplate = new BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
                }

                if (md.isIgnored()) {
                    result.put(md.configKey(), (args) -> {
                        throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
                    });
                } else {
                    result.put(md.configKey(), this.factory.create(target, md, (RequestTemplate.Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
                }
            }

            return result;
        }

this.contract.parseAndValidateMetadata(target.type())表示获取当前对象的方法

最后返回 类名#方法名 为key,MethodHandler 为value的map

存入map的时候调用的方法是

 this.factory.create(key, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder))

点进去这个 create 方法,我们看到是new了一个对象,这个类是实现了MethodHandler

return new SynchronousMethodHandler(target, this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, this.decode404, this.closeAfterDecode, this.propagationPolicy);

而这一个 apply我们会走很多遍,一个远程接口每个方法都会走一遍

1.11 this.factory.create(target, methodToHandler)

我们再返回newInstance 方法,可以看到最终是调用了一个 this.factory.create 来创建 InvocationHandler,InvocationHandlerFactory 的 create 里面自己就对这个接口进行实现 return new FeignInvocationHandler(target, dispatch);

1.12 FeignInvocationHandler

 FeignInvocationHandler 实现了 InvocationHandler 接口,重写了里面的 invoke 方法

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (!"equals".equals(method.getName())) {
                if ("hashCode".equals(method.getName())) {
                    return this.hashCode();
                } else {
                    return "toString".equals(method.getName()) ? this.toString() : ((InvocationHandlerFactory.MethodHandler)this.dispatch.get(method)).invoke(args);
                }
            } else {
                try {
                    Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                    return this.equals(otherHandler);
                } catch (IllegalArgumentException var5) {
                    return false;
                }
            }
        }

 可以看到它最终是调用MethodHandler的 invoke方法。至此我们的第一部分算是走完了。

2、远程调用实现原理

2.1 SynchronousMethodHandler

由于上面FeignInvocationHandler.invoke()最后返回的是((InvocationHandlerFactory.MethodHandler)this.dispatch.get(method)).invoke(args),而这个MethodHandler实际上就是SynchronousMethodHandler

发现最后方法参数会在this.buildTemplateFromArgs.create(argv);返回请求模板,最后去访问executeAndDecode方法,也就是真正执行http请求的方法执行并且解码。

 执行真正的http请求,至此feign调用原理走完了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值