Feign源码解析——初始化流程
首先我们在使用Feign的时候会加上两个注解,第一个是@EnableFeignClients
开启Feign的功能,第二个是在修饰被调用方的接口加上这个注解@FeignClient
而生成代理类。那么我们就根据这两个注解为入口进行解析。
@EnableFeignClients注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
…………
}
@Import注解就是之前xml配置中的import标签,可以用于依赖第三方包中bean的配置和加载
进入@EnableFeignClients
注解我们可以发现里面加载了FeignClientsRegistrar
类,进去FeignClientsRegistrar
类,我们看到他实现了ImportBeanDefinitionRegistrar
这个类,ImportBeanDefinitionRegistrar
接口是Spring的一个扩展接口,通过registerBeanDefinitions
方法向容器中注入Bean。
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
……………………………省略…………………………………
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
………………………………省略……………………………………
}
所以我们可以理解这一步就是将程序中带有@FeignClient
注解的接口注册到Spring容器中。接下来我们深入了解他是如何将什么信息注册进去了
其中registerDefaultConfiguration
方法注册了@EnableFeignClients
注解中defaultConfiguration
属性,重点我们看registerFeignClients
方法。
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set basePackages;
//取出EnableFeignClients注解中的信息
Map attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
//设置要扫描的注解是@FeignClient
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
//查看@EnableFeignClients中设置的clients参数
final Class>[] clients = attrs == null ? null
: (Class>[]) attrs.get("clients");
//如果没有设置clients参数,那么设置要扫描的包是@EnableFeignClients中设置的value值
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
final Set clientClasses = new HashSet<>();
basePackages = new HashSet<>();
for (Class> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
@Override
protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(
new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
}
for (String basePackage : basePackages) {
//扫描设置的包中的@FeignClient注解
Set candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// 验证@ FeignClient注解的类是一个接口
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
//获得@ FeignClient所设置的属性
Map attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
//获得@FeignClient中设置的value值
String name = getClientName(attributes);
//将@FeignClient注解中Configuration属性注册进容器中,其中名字是name的值
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//下面有讲解
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
registerFeignClient
方法讲解,其实这一块就是将@FeignClient
注解中所有的value值取出并且放入容器中。在这里我们需要注意此时注册是FeignClientFactoryBean
类型,之前注册的都是FeignClientSpecification
类型的
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
到这Feign的初始化流程已经说完了。大概总结一下Feign的初始化流程
- 读取
@EnableFeignClients
注解中basePackage值下的所有带有@FeignClient
的接口 - 读取接口上面的
@FeignClient
注解参数 - 如果此接口上有Configuration参数,那么先进行注册此参数,注意此参数注册在Spring容器中是以
FeignClientSpecification
类型注册的 - 注册完Configuration参数以后,然后将其余的信息注册到容器中,注意这时是以
FeignClientFactoryBean
类型注册的,另外此时的Configuration参数并没有传过来。
参考文章
- https://xli1224.github.io/2017/09/14/feign-anaylsis/
- https://blog.csdn.net/forezp/article/details/73480304
- https://www.jianshu.com/p/f72d89265166