• Spring-Cloud之Feign原理剖析


     

    Feign 主要是帮助我们方便进行rest api服务间的调用,其大体实现思路就我们通过标记注解在一个接口类上(注解上将包含要调用的接口信息),之后在调用时根据注解信息组装好请求信息,接下来基于ribbon这些负载均衡器来生成真实的服务地址,最后将请求发送出去;之后将接收到的结果反序列化为相关的Java对象供我们直接使用。 下面我们走进Spring Cloud对feign封装的源码中去了解其主要实现机制。

    Feign的大体机制

    通过在启动类上标记 @EnableFeignClients 注解来开启feign的功能,服务启动后会扫描 @FeignClient 注解标记的接口,然后根据扫描的注解信息为每个接口类生成feign客户端请求,同时解析接口方法中的Spring MVC的相关注解,通过专门的注解解析器识别这些注解信息,以便后面可以正确的组装请求参数,使用 Ribbon 和 Eureka 获取到请求服务的真实地址等信息,最后使用 http 相关组件进行执行调用。其大致流程图如下:

    @EnableFeignClients 和 @FeignClient 注解

    在EnableFeignClients 注解类中有一个 @Import(
    FeignClientsRegistrar.class)的配置

     

    1. @Retention(RetentionPolicy.RUNTIME)
    2. @Target(ElementType.TYPE)
    3. @Documented
    4. // 引入FeignClientsRegistrar 来扫描@FeignClient注解下的类
    5. @Import(FeignClientsRegistrar.class)
    6. public @interface EnableFeignClients {
    7. ...
    8. }

    我们追踪代码进入到FeignClientsRegistrar类中,会发现FeignClientsRegistrar 类实现了
    ImportBeanDefinitionRegistrar(在spring context 项目中)接口,因此spring boot启动时会调用它的registerBeanDefinitions()方法,该方法中会扫描 EnableFeignClients 和 FeignClient 注解信息并设置相关信息。

    1. /**
    2. * spring boot 启动时会自动调用 ImportBeanDefinitionRegistrar 入口方法
    3. */
    4. @Override
    5. public void registerBeanDefinitions(AnnotationMetadata metadata
    6. , BeanDefinitionRegistry registry) {
    7. // 读取 @EnableFeignClients 注解中信息
    8. registerDefaultConfiguration(metadata, registry);
    9. // 扫描所有@FeignClient注解的类
    10. registerFeignClients(metadata, registry);
    11. }

    registerDefaultConfiguration方法


    registerDefaultConfiguration()方法中会读取@EnableFeignClients注解信息,然后将这些信息注册到一个 BeanDefinitionRegistry 里面去;之后feign的一些默认配置将通过这里注册的信息中去获取。

    registerFeignClients方法

    • registerFeignClients()方法会扫描相关包路径(如果EnableFeignClients的basePackages没有配置,默认会直接使用启动类所在的包路径)下所有的@FeiginClient注解的类
    • 然后根据@FeiginClient注解信息向BeanDefinitionRegistry里面注册bean,注意这里设置的bean名称生成规则是使用服务名+FeignClientSpecification.class.getSimpleName(),因此如果对一个服务写多个接口类会发生bean名称重复导致注册失败。所以需要增加一个 allow-bean-definition-overriding: true 的配置。
    • 最后会调用 registerFeignClient() 方法注册feign客户端,这里的bean名称的为当前接口类的类路径。

    其流程图如下:

    feign客户端的动态代理

    上面registerFeignClient()方法中在构建bean的时候,实际构建的是FeignClientFactoryBean。

    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
    

    FeignClientFactoryBean 类对父类的getObject()方法进行了重写,后面动态代理时使用的就是它来获取feign client的。在这里会根据上面注解配置,同时会读取application.yml配置信息,根据配置来设置feign的相关信息,比如编解码器、注解解析器、请求超时时间等;之后如果没有设置url那么就会和负载均衡器(ribbon)整合。最后会通过反射将接口中相关方法进行解析保存供后面进行jdk代理使用。

    1. @Override
    2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    3. // 判断是否是不需要代理的
    4. if ("equals".equals(method.getName())) {
    5. try {
    6. Object otherHandler =
    7. args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
    8. return equals(otherHandler);
    9. } catch (IllegalArgumentException e) {
    10. return false;
    11. }
    12. } else if ("hashCode".equals(method.getName())) {
    13. return hashCode();
    14. } else if ("toString".equals(method.getName())) {
    15. return toString();
    16. }
    17. // 需要代理,执行代理方法
    18. return dispatch.get(method).invoke(args);
    19. }

     

     

  • 相关阅读:
    MinIO:开源对象存储解决方案的领先者
    第16篇ESP32 platformio_arduino框架 wifi联网_连接WiFi热点并连接tcp server收发数据进行通讯
    flex的用法 代码6
    Tensorflow图像识别 Tensorflow手写体识别(二)
    CANOE使用七:自动化测试Autosar网络管理(创建TestModule-搭配Panel界面及使用Capl识别配置文件TXT的自动化测试流程)
    Redis主从复制流程
    内网渗透之Linux反弹shell(三)
    [附源码]SSM计算机毕业设计归元种子销售管理系统JAVA
    【无标题】
    C++ QT学习笔记
  • 原文地址:https://blog.csdn.net/m0_67698950/article/details/125558483