Feign源码分析-接口如何发现并生成代理类

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Feign源码分析-接口如何发现并生成代理类

1写作目录


之前自己写过一个RPC框架demo,遇到两个问题没有解决。


在consumer端怎么找到被代理的接口呢?

比如用这个@FeignClient注解,正常情况下Spring是识别不到的,那是怎么识别到的呢?


接口如何代理呢?

之前的动态代理和静态代理都是先生成一个类,然后在去代理,但是在consumer端是没有接口实现类的,那怎么实现代理的呢?


因为解决这两个问题,也因为一些机缘巧合,看了部分Feign的源码,从而理解了这其中的逻辑,下面给大家分析并记录一下这个问题。


2前提


了解SpringBoot的自动装配原理,否则跟不上


3环境搭建


下载地址:https://github.com/cbeann/SpringCloudDemoHoxton


如下图所示,其中Service层就是Feign接口,Controller层调用Service的Feign接口


0.png


4 源码分析


4.1如何找到@FeignClient标注的接口


4.1.1添加注解引入目标类


在consumer端一般会加@EnableFeignClients注解,其实这是一个复合注解,点进去看一下可以发现


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)


引入了FeignClientsRegistrar.class这个类并加载到IOC容器中。然后就会到达registerBeanDefinitions方法并在该方法里调用registerFeignClients方法


//FeignClientsRegistrar.java
@Override
  public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
    registerDefaultConfiguration(metadata, registry);
    registerFeignClients(metadata, registry);
  }


4.1.2寻找代理接口


1)首先获取带有EnableFeignClients注解的启动类的包路径


//FeignClientsRegistrar###registerFeignClients
        Set<String> basePackages;
    Map<String, Object> attrs = metadata
        .getAnnotationAttributes(EnableFeignClients.class.getName());
    basePackages = getBasePackages(metadata);


2)然后获取.class文件并解析。

下图为ClassPathScanningCandidateComponentProvider##scanCandidateComponents方法。


如果找到有@FeignClient注解的则解析为ScannedGenericBeanDefinition


01.png


3)然后对上面的每一个ScannedGenericBeanDefinition进行二次封装

此时到达FeignClientsRegistrar的registerFeignClient方法,把每一个ScannedGenericBeanDefinition封装为AbstractBeanDefinition,注意,此时的class类型为FeignClientFactoryBean.class


02.png


4)此时我们就明白了,Spring原来是通过解析.class文件获取@FeignClient注解的interface并解析为类型为FeignClientFactoryBean.class的BeanDefinition,剩下的就是Bean的生命周期了。


4.2FeignClientFactoryBean的生命周期


我们先看一下FeignClientFactoryBean类的继承关系


class FeignClientFactoryBean
    implements FactoryBean<Object>, InitializingBean, ApplicationContextAware


这个类继承了FactoryBean接口,那么在Bean的生命周期里肯定会调用FactoryBean的getObject方法,此时我们就是在FactoryBean的getObject方法上打断点,从这里开始debug


00.png


因为这是Bean的生命周期(了解Bean的生命周期并从调用栈可以看出),所以我们一直从上面的那个地方跟命令是return的方法,一直跟到ReflectiveFeign的newInstance方法,如下图所示,有没有发现,这个地方是不是似曾相识(不相识的话说明动态代理不熟)?


000.png


5总结


Feign是通过扫描.class文件定位到标有@FeignClient注解的类的,然后解析为类型为FeignClientFactoryBean.class的BeanDefinition,然后执行Bean的生命周期,最后调用FeignClientFactoryBean的getObject方法进行动态代理。

拓展:其实也可以通过BeanPostProcessor去实现上面的功能,当然不让上面的完美


其实远程调用有一个统一面临的问题,就是你是不知道远程调用类是什么类型的,那么怎么对这个类执行Bean的生命周期呢?或者换换句话说,BeanDefinition里面的类是哪个类呢?如果要做到统一,则可以使用FactoryBean接口,让类的创建发生延迟,其实Dobbo的源码中也是通过FactoryBean实现的。


目录
相关文章
|
设计模式 监控 前端开发
SpringBoot拦截器和动态代理有什么区别?
SpringBoot拦截器和动态代理有什么区别?
96 0
|
Dubbo Java 数据库连接
利用FactoryBean接口实例化,来实现dubbo接口调用和mybatis接口调用
Java编程规范中声明,Java接口类是不能直接实例化的,但是我们在平时的开发中经常会遇到只声明接口就可以直接使用的。 eg: 1. Mybatis中只用使用`@MapperScan`声明要扫描的Mapper接口类就可以直接从Spring中获取使用,进行操作数据库 2. Dubbo中只要用Dubbo提供的`@Service`注解,同样可以直接从Spring中获取使用进行远程调用。
405 0
|
2月前
|
存储 NoSQL Java
aspect实现mock-feign接口
该代码为一个用于Feign接口的模拟(Mock)实现类`FeignMockAspect`,通过切面编程方式对带有`@FeignClient`注解的接口提供模拟响应。在非生产环境中,根据特定配置从Redis中获取Mock数据并转换为对应类型的对象返回,以减少对外部系统的依赖和提高测试效率。使用Hutool工具类和Spring Data Redis进行数据处理与存储操作。
|
4月前
|
分布式计算 Java MaxCompute
详解 Java 限流接口实现问题之在Spring框架中使用AOP来实现基于注解的限流问题如何解决
详解 Java 限流接口实现问题之在Spring框架中使用AOP来实现基于注解的限流问题如何解决
|
6月前
|
数据中心
Feign调用
Feign调用
33 0
|
6月前
|
Java Linux iOS开发
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
48 0
|
负载均衡 Nacos
一起用feign来调用接口(有源码)
nacos很好的兼容了feign,feign默认集成了Ribbon,所以Nacos下使用Feign就默认实现了负载均衡 一、测试结果
131 0
一起用feign来调用接口(有源码)
|
Java
从源码分析JDK动态代理
从源码分析JDK动态代理
169 0
从源码分析JDK动态代理
JDK动态代理为什么只能代理有接口的类?
JDK动态代理为什么只能代理有接口的类?
|
编解码 Dubbo Java
动态代理与RPC
动态代理与RPC