Feign的核心加载流程
上篇文章完成了Feign整体流程的的一个大图,本节主要来研究下@FeignClient,是如何被加载到Spring容器中的
实现Feign涉及到两个核心注解
@EnableFeignClients和@FeignClient就从这两个类入口开始
目录
- 入口类
- 核心类的扫描
1.寻找加载的入口类
该注解标注在服务启动入口,会将标注了
@FeignClient相关的接口扫描到容器中
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(FeignClientsRegistrar.class)public @interface EnableFeignClients {String[] value() default {};String[] basePackages() default {};Class<?>[] basePackageClasses() default {};Class<?>[] defaultConfiguration() default {};Class<?>[] clients() default {};}
- 这里可以看到核心类是
FeignClientsRegistrar - 其他的几个参数,可以在
@EnableFeignClients中指定相关参数
这里我们可以看到@EnableFeignClients注解包含了几个参数,这里其实我们可以实现自定义的一些功能。那么接下来去看下FeignClientsRegistrar中的实现
2.核心注解的启动入口FeignClientsRegistrar类
class FeignClientsRegistrarimplements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {registerDefaultConfiguration(metadata, registry);registerFeignClients(metadata, registry);}}
该类通过实现Spring的相关组件,使得在服务启动时会执行registerBeanDefinitions()的方法,该方法内部通过两个私有方法实现具体功能。这里有个小技巧,一般是去寻找@Override的方法
2.1.registerDefaultConfiguration()
private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {//获取到注解@EnableFeignClients中的所有属性Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {String name;if (metadata.hasEnclosingClass()) {name = "default." + metadata.getEnclosingClassName();}else {name = "default." + metadata.getClassName();}//这里的name就是`default.包名`//将name和defaultAttrs注册到BeanDefinitionRegistry中去registerClientConfiguration(registry, name,defaultAttrs.get("defaultConfiguration"));}}
- 1.获取到服务启动类的全限定名:
default.xxx.EurekaClientApplication - 2.解析@EnableFeignClients注解的属性
- 3.将上面获取的信息注册到BeanDefinitionRegistry中
2.2.registerFeignClients()
该方法完成了@FeignClient相关接口的实例化以及注册功能
扫描@FeignClient的
public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {//获取sanner,该scanner只扫描@FeignClient注解的类ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);Set<String> basePackages;//扫描@FeignClient标注的所有接口的全限定名....for (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {//核心注册到spring容器的类registerFeignClient(registry, annotationMetadata, attributes);}}}}
- 1.获取到scaner,只扫描@FeignClient标注的
- 2.用scanner扫描@FeignClient,并将其路径名放入set中
- 3.将上面获取的信息注册到BeanDefinitionRegistry中
注册到容器 registerFeignClient()
private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {String className = annotationMetadata.getClassName();BeanDefinitionBuilder definition = BeanDefinitionBuilder//这里是核心代理类,FeignClientFactoryBean是后续生成@FeignClient代理类的工厂.genericBeanDefinition(FeignClientFactoryBean.class);//基于@FeignClient的一些属性和OrderClient的一些信息构建`BeanDefinitionBuilder`.....//构造holder,完成BeanDefinition的注册BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,new String[] { alias });BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}
- 基于@FeignClient的一些属性和OrderClient的一些信息构建BeanDefinitionBuilder
- 构造BeanDefinitionHolder,完成BeanDefinition的注册
3.总结
一张图来总结整个Feign加载的流程

我们从入口类@EnableFeignClients中找到了核心的类FeignClientsRegistrar,然后从中找到了对应的入口方法。这里其实核心的操作分为两个流程
- 处理
@EanbleFeignClients,这里找到核心入口类FeignClientsRegistrar- 解析
@EanbleFeignClients入口,若有指定的参数basePackage等,则获取到待扫描的路径名,若无,则取springboot启动类路径 - 解析
@EnableFeignClients对应的相关参数
- 解析

