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

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注解,其实这是一个复合注解,点进去看一下可以发现

@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
在这里插入图片描述
3)然后对上面的每一个ScannedGenericBeanDefinition进行二次封装
此时到达FeignClientsRegistrar的registerFeignClient方法,把每一个ScannedGenericBeanDefinition封装为AbstractBeanDefinition,注意,此时的class类型为FeignClientFactoryBean.class
在这里插入图片描述
4)此时我们就明白了,Spring原来是通过解析.class文件获取@FeignClient注解的interface并解析为类型为FeignClientFactoryBean.classBeanDefinition,剩下的就是Bean的生命周期了。

4.2FeignClientFactoryBean的生命周期

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

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框架

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CBeann

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值