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 FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public 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
对应的相关参数
- 解析