前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Feign源码分析-接口如何发现并生成代理类

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

作者头像
CBeann
发布2023-12-25 19:25:13
1090
发布2023-12-25 19:25:13
举报
文章被收录于专栏:CBeann的博客CBeann的博客

1写作目录

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

  • 在consumer端怎么找到被代理的接口呢? 比如用这个@FeignClient注解,正常情况下Spring是识别不到的,那是怎么识别到的呢?
  • 接口如何代理呢? 之前的动态代理和静态代理都是先生成一个类,然后在去代理,但是在consumer端是没有接口实现类的,那怎么实现代理的呢?

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

2前提

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

3环境搭建

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

参考视频:feign环境搭建demo

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

4 源码分析

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

4.1.1添加注解引入目标类

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

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)

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

代码语言:javascript
复制
//FeignClientsRegistrar.java
@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}
4.1.2寻找代理接口

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

代码语言:javascript
复制
//FeignClientsRegistrar###registerFeignClients
        Set<String> basePackages;
		Map<String, Object> attrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName());
		basePackages = getBasePackages(metadata);

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

下图为ClassPathScanningCandidateComponentProvider##scanCandidateComponents方法。

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

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

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

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

4.2FeignClientFactoryBean的生命周期

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

代码语言:javascript
复制
class FeignClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware

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

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

5总结

  • Feign是通过扫描.class文件定位到标有@FeignClient注解的类的,然后解析为类型为FeignClientFactoryBean.classBeanDefinition,然后执行Bean的生命周期,最后调用FeignClientFactoryBean的getObject方法进行动态代理。 拓展:其实也可以通过BeanPostProcessor去实现上面的功能,当然不让上面的完美
  • 其实远程调用有一个统一面临的问题,就是你是不知道远程调用类是什么类型的,那么怎么对这个类执行Bean的生命周期呢?或者换换句话说,BeanDefinition里面的类是哪个类呢?如果要做到统一,则可以使用FactoryBean接口,让类的创建发生延迟,其实Dobbo的源码中也是通过FactoryBean实现的。

6参考

诸葛老师VIP微服务调用组件Ribbon和Feign源码剖析

尚硅谷SpringCloud框架开发教程(SpringCloudAlibaba微服务分布式架构丨Spring Cloud)

花了一星期,自己写了个简单的RPC框架

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-12-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1写作目录
  • 2前提
  • 3环境搭建
  • 4 源码分析
    • 4.1如何找到@FeignClient标注的接口
      • 4.1.1添加注解引入目标类
      • 4.1.2寻找代理接口
    • 4.2FeignClientFactoryBean的生命周期
    • 5总结
    • 6参考
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档