美文网首页
Spring Cloud之Feign源码分析

Spring Cloud之Feign源码分析

作者: loveFXX | 来源:发表于2020-03-14 21:32 被阅读0次

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


    EnableFeignClients.png

    EnableFeignClients注解

    EnableFeignClients.png

    EnableFeignClients注解主要作用是导入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方法)

    相关文章

      网友评论

          本文标题:Spring Cloud之Feign源码分析

          本文链接:https://www.haomeiwen.com/subject/caloshtx.html