Feign源码从示例代码启动类中@EnableFeignClients注解开始分析
EnableFeignClients.png
EnableFeignClients注解
EnableFeignClients.pngEnableFeignClients注解主要作用是导入FeignClientsRegistrar类
FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware。由于实现了ImportBeanDefinitionRegistrar,所以在spring Bean的生命周期时,会调用registerBeanDefinitions方法。
registerBeanDefinitions
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
registerBeanDefinitions.png
1、首先调用registerDefaultConfiguration()方法根据默认元数据信息注册默认配置类
注册的beanName是default.com.AppClient.FeignClientSpecification,BeanDefinition是org.springframework.cloud.openfeign.FeignClientSpecification的类信息注册到spring上下文环境中
2、然后再向spring容器注册FeignClients对象
1、registerDefaultConfiguration
FeignClientsRegistrar#registerDefaultConfiguration
registerDefaultConfiguration.png
这个方法,首先获取EnableFeignClients注解类的包含的属性键值对,拼接name,之后注册registerClientConfiguration客户端配置
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerClientConfiguration
registerClientConfiguration.png
注册的beanName是default.com.AppClient.FeignClientSpecification,BeanDefinition是org.springframework.cloud.openfeign.FeignClientSpecification完成了Feign客户端配置的注入
2、registerFeignClients
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients
registerFeignClients1.png
这个方法主要步骤:
①、首先创建一个scanner对象,这里只是创建一个对象,并没有执行isCandidateComponent方法。不在这里调用
FeignClientsRegistrar#getScanner方法创建scanner对象
image.png
new一个ClassPathScanningCandidateComponentProvider对象
image.png
②、获取EnableFeignClients注解的属性值,一共有5组。其中的键包括
"value" 、"defaultConfiguration" 、"clients" 、"basePackages" 、"basePackageClasses"
③、创建FeignClient类型的过滤器对象annotationTypeFilter
registerFeignClients2.png
④、如果clients配置了属性值,则会走else,像下面这种配置形式。如果clients没有配置这样的属性值,则会执行添加过滤器和获取扫描的包
image.png
FeignClientsRegistrar#registerFeignClients
registerFeignClients2.png
⑤、遍历com类包并调用①创建scanner对象的findCandidateComponents方法,获取符合条件接口的BeanDefinition信息
ClassPathScanningCandidateComponentProvider#findCandidateComponents
findCandidateComponents.png
ClassPathScanningCandidateComponentProvider#scanCandidateComponents
扫描出packageSearchPath所有资源文件,然后进行遍历资源文件。经过两步判断:isCandidateComponent(MetadataReader)和isCandidateComponent()判断
scanCandidateComponents.png
image.png
首先调用isCandidateComponent(MetadataReader)方法,进行过滤器刷选是FeignClient类型的
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader)
isCandidateComponent.png
然后再调用了创建scanner类的重写的isCandidateComponent方法判断
isCandidateComponent.png
最终得到的是含有FeignClient注解的接口,添加到Set<BeanDefinition> candidates集合中。
⑥、遍历第⑤步得到FeignClient注解的接口的candidateComponent集合,获取FeignClient注解的属性值attributes
candidateComponent.png
⑦、在attributes属性值中获取调用的微服务名称name。然后注册客户端配置
注册结果是BeanName是user.FeignClientSpecification,beanClass 是org.springframework.cloud.openfeign.FeignClientSpecification。
所以到这里一共注册了两个beanClass 是FeignClientSpecification类的元数据信息,另一个BeanName是default.com.AppClient.FeignClientSpecification
registerClientConfiguration.png
⑧、注册当前类的BeanDefinition注册到spring容器
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
获取当前注解类的类名className,然后创建BeanDefinitionBuilder类
registerFeignClient.png
创建builder的definition设置的beanClass是FeignClientFactoryBean工厂类,在使用时会通过@Autowired注解注入到类中,FactoryBean工厂Bean的使用机制就是会调用FactoryBean#getObject方法获得类对象,通过FactoryBean#getObjectType方法获得类型
org.springframework.beans.factory.support.BeanDefinitionBuilder
BeanDefinitionBuilder.png
⑨、对创建的definition类进行属性设置,自动注入模型是2(AUTOWIRE_BY_TYPE)
image.png
⑩、BeanName是类名com.feign.FeignUserClient(此时调用还是加了@FeignClient注解的类集合的遍历过程),beanDefinition设置属性后的BeanDefinitionBuilder封装成BeanDefinitionHolder对象,注册spring容器中
image.png
当使用时@Autowired注入FeignUserClient类时,便会触发调用FeignClientFactoryBean的工厂类方法获取对象
image.png
获取代理类完成属性注入
org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
getObject.png
org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget
image.png
这个方法主要步骤:
①、从当前应用上下文环境applicationContext获取FeignContext类的Feign上下文,从BeanFactory获取。此时FeignContext的类对象,在FeignAutoConfiguration注解中可以通过@Bean获取。FeignAutoConfiguration在当前包的spring.factory完成自动扫描
FeignAutoConfiguration.png
此时的FeignContext的contexts并没有属性值,将会通过接下来的feign(context)方法完成
FeignContext.png
②、创建Feign.Builder对象,并完成FeignContext的contexts的赋值
org.springframework.cloud.openfeign.FeignClientFactoryBean#feign
feign.png
org.springframework.cloud.openfeign.FeignClientFactoryBean#get
image.png
org.springframework.cloud.context.named.NamedContextFactory#getInstance
image.png
org.springframework.cloud.context.named.NamedContextFactory#getContext
getContext.png
org.springframework.cloud.context.named.NamedContextFactory#createContext
image.png
注册class org.springframework.cloud.openfeign.FeignClientsConfiguration配置类,设置父容器完成父子隔离,刷新容器完成spring子容器环境的初始化。与前面的Ribbon设置父子容器相同。
FeignContext.png
Feign.Builder.png
这个方法过程就是在FeignContext(Feign环境)中设置微服务间调用的父子容器,并设置contexts属性值
③、接下来根据@FeignClient注解中对url或name参数判断并拼接url。组合HardCodedTarget对象、调用loadBalance方法
getTarget.png
type类型的获取,是通过FeignClientFactoryBean#getObjectType方法
④、loadBalance
org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance
loadBalance.png
org.springframework.cloud.openfeign.FeignClientFactoryBean#getOptional
在当前子容器中获取Client的实现类LoadBalancerFeignClient,
getOptional.png
这个类是在DefaultFeignLoadBalancedConfiguration注解中注入。DefaultFeignLoadBalancedConfiguration注解是在自动扫描当前包FeignLoadBalancerAutoConfiguration中Import的
FeignBlockingLoadBalancerClient.png
⑤、HystrixTargeter#target
获取HystrixTargeter,并执行target方法
image.png
org.springframework.cloud.openfeign.HystrixTargeter#target
target.png
feign.Feign.Builder#target(feign.Target<T>)
image.png
feign.ReflectiveFeign#newInstance
生产代理对象
image.png
声明式微服务间调用
image.png便会走到invoke方法调用
feign.ReflectiveFeign.FeignInvocationHandler#invoke
invoke.png
feign.SynchronousMethodHandler#invoke
invoke.png
获取Options对象用到了Stream流式计算
feign.SynchronousMethodHandler#executeAndDecode
executeAndDecode.png
org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute
execute.png
com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer
image.png
接下来调用便用到了rxjava包
总结:
Feign是声明式的微服务间调用,源码过程主要分为三个阶段:
1、环境初始化(设置父子容器实现容器隔离)
2、代理类注入(设置工厂Bean类,使用JDK动态代理完成注入)
3、调用过程(调用代理类的invoke方法)
网友评论