Spring Cloud之Feign源码分析
Feign源码从示例代码启动类中@EnableFeignClients注解开始分析

EnableFeignClients注解

EnableFeignClients注解主要作用是导入FeignClientsRegistrar类
FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware。由于实现了ImportBeanDefinitionRegistrar,所以在spring Bean的生命周期时,会调用registerBeanDefinitions方法。
registerBeanDefinitions
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions

1、首先调用registerDefaultConfiguration()方法根据默认元数据信息注册默认配置类
注册的beanName是default.com.AppClient.FeignClientSpecification,BeanDefinition是org.springframework.cloud.openfeign.FeignClientSpecification的类信息注册到spring上下文环境中
2、然后再向spring容器注册FeignClients对象
1、registerDefaultConfiguration
FeignClientsRegistrar#registerDefaultConfiguration

这个方法,首先获取EnableFeignClients注解类的包含的属性键值对,拼接name,之后注册registerClientConfiguration客户端配置
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerClientConfiguration

注册的beanName是default.com.AppClient.FeignClientSpecification,BeanDefinition是org.springframework.cloud.openfeign.FeignClientSpecification完成了Feign客户端配置的注入
2、registerFeignClients
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients

这个方法主要步骤:
①、首先创建一个scanner对象,这里只是创建一个对象,并没有执行isCandidateComponent方法。不在这里调用
FeignClientsRegistrar#getScanner方法创建scanner对象

new一个ClassPathScanningCandidateComponentProvider对象

②、获取EnableFeignClients注解的属性值,一共有5组。其中的键包括
"value" 、"defaultConfiguration" 、"clients" 、"basePackages" 、"basePackageClasses"
③、创建FeignClient类型的过滤器对象annotationTypeFilter

④、如果clients配置了属性值,则会走else,像下面这种配置形式。如果clients没有配置这样的属性值,则会执行添加过滤器和获取扫描的包

FeignClientsRegistrar#registerFeignClients

⑤、遍历com类包并调用①创建scanner对象的findCandidateComponents方法,获取符合条件接口的BeanDefinition信息
ClassPathScanningCandidateComponentProvider#findCandidateComponents

ClassPathScanningCandidateComponentProvider#scanCandidateComponents
扫描出packageSearchPath所有资源文件,然后进行遍历资源文件。经过两步判断:isCandidateComponent(MetadataReader)和isCandidateComponent()判断


首先调用isCandidateComponent(MetadataReader)方法,进行过滤器刷选是FeignClient类型的
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader)

然后再调用了创建scanner类的重写的isCandidateComponent方法判断

最终得到的是含有FeignClient注解的接口,添加到Set<BeanDefinition> candidates集合中。
⑥、遍历第⑤步得到FeignClient注解的接口的candidateComponent集合,获取FeignClient注解的属性值attributes

⑦、在attributes属性值中获取调用的微服务名称name。然后注册客户端配置
注册结果是BeanName是user.FeignClientSpecification,beanClass 是org.springframework.cloud.openfeign.FeignClientSpecification。
所以到这里一共注册了两个beanClass 是FeignClientSpecification类的元数据信息,另一个BeanName是default.com.AppClient.FeignClientSpecification

⑧、注册当前类的BeanDefinition注册到spring容器
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
获取当前注解类的类名className,然后创建BeanDefinitionBuilder类

创建builder的definition设置的beanClass是FeignClientFactoryBean工厂类,在使用时会通过@Autowired注解注入到类中,FactoryBean工厂Bean的使用机制就是会调用FactoryBean#getObject方法获得类对象,通过FactoryBean#getObjectType方法获得类型
org.springframework.beans.factory.support.BeanDefinitionBuilder

⑨、对创建的definition类进行属性设置,自动注入模型是2(AUTOWIRE_BY_TYPE)

⑩、BeanName是类名com.feign.FeignUserClient(此时调用还是加了@FeignClient注解的类集合的遍历过程),beanDefinition设置属性后的BeanDefinitionBuilder封装成BeanDefinitionHolder对象,注册spring容器中

当使用时@Autowired注入FeignUserClient类时,便会触发调用FeignClientFactoryBean的工厂类方法获取对象

获取代理类完成属性注入
org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject

org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget

这个方法主要步骤:
①、从当前应用上下文环境applicationContext获取FeignContext类的Feign上下文,从BeanFactory获取。此时FeignContext的类对象,在FeignAutoConfiguration注解中可以通过@Bean获取。FeignAutoConfiguration在当前包的spring.factory完成自动扫描

此时的FeignContext的contexts并没有属性值,将会通过接下来的feign(context)方法完成

②、创建Feign.Builder对象,并完成FeignContext的contexts的赋值
org.springframework.cloud.openfeign.FeignClientFactoryBean#feign

org.springframework.cloud.openfeign.FeignClientFactoryBean#get

org.springframework.cloud.context.named.NamedContextFactory#getInstance

org.springframework.cloud.context.named.NamedContextFactory#getContext

org.springframework.cloud.context.named.NamedContextFactory#createContext

注册class org.springframework.cloud.openfeign.FeignClientsConfiguration配置类,设置父容器完成父子隔离,刷新容器完成spring子容器环境的初始化。与前面的Ribbon设置父子容器相同。


这个方法过程就是在FeignContext(Feign环境)中设置微服务间调用的父子容器,并设置contexts属性值
③、接下来根据@FeignClient注解中对url或name参数判断并拼接url。组合HardCodedTarget对象、调用loadBalance方法

type类型的获取,是通过FeignClientFactoryBean#getObjectType方法
④、loadBalance
org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance

org.springframework.cloud.openfeign.FeignClientFactoryBean#getOptional
在当前子容器中获取Client的实现类LoadBalancerFeignClient,

这个类是在DefaultFeignLoadBalancedConfiguration注解中注入。DefaultFeignLoadBalancedConfiguration注解是在自动扫描当前包FeignLoadBalancerAutoConfiguration中Import的

⑤、HystrixTargeter#target
获取HystrixTargeter,并执行target方法

org.springframework.cloud.openfeign.HystrixTargeter#target

feign.Feign.Builder#target(feign.Target<T>)

feign.ReflectiveFeign#newInstance
生产代理对象

声明式微服务间调用

便会走到invoke方法调用
feign.ReflectiveFeign.FeignInvocationHandler#invoke

feign.SynchronousMethodHandler#invoke

获取Options对象用到了Stream流式计算
feign.SynchronousMethodHandler#executeAndDecode

org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute

com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer

接下来调用便用到了rxjava包
总结:
Feign是声明式的微服务间调用,源码过程主要分为三个阶段:
1、环境初始化(设置父子容器实现容器隔离)
2、代理类注入(设置工厂Bean类,使用JDK动态代理完成注入)
3、调用过程(调用代理类的invoke方法)