刚到新的公司有点忙,这周终于有时间结合SS老师的课程来学习源码。来吧,继续学起来。
一、feign的启动流程
上一期讲的是Feign的猜测的启动流程,现在,直接从源码入手。
上一系列的图,希望不会晕车。其实,我觉得看源码其实挺难的,特别是看别人的博客文章,简直一头雾水,菜鸡的我一样是有这种问题。所以我会边学习边输出,这样也是自己最好的一个学习方法。
从FeignClients注解就有一个关键的 @import 标签。
进入 FeignClientsRegistrar 这个类中。
我们可以看到 FeignClientsRegistrar 这个类中,进行了ImportBeanDefinitionRegistrar 的接口实现
OK, 紧接着就是调用了下面的 FeignClientsRegistrar#registerBeanDefinitions 方法,进行一个重写。
PS: 因为Spring上下文启动的时候,会调用这个方法进行相关的初始化
OK, 回到正文,就从FeignClientsRegistrar#registerBeanDefinitions 方法开始进行分析
二、registerBeanDefinitions分析
2.1 registerDefaultConfiguration(metadata, registry);
直接上代码:
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 获取@EnableFeignClients上的attrs属性值,封装到对应的一个Map中
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();
}
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
其实这里没啥特别重要的:
- 把@EnableFeignClients注解上的属性,进行一个属性的注入,注入到Map容器上。 这儿需要关注一个重点对象: metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); 批注①
2.2 registerFeignClients(metadata, registry)分析
这里面代码太长,一下子拉所有的代码出来比较难懂,所以这里使用局部分析的方式。
2.2.1 扫描属性
public void registerFeignClients(..){
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
}
// 省略以下的代码
....
registerFeignClient(...)
重点关注这行代码:
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
刚刚我们提到 批注①做的事情,就是将@EnableFeignClients注解上的属性丢到Map上去进行保存。
关注到以下代码:
Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
那么就是获得加了@EnableFeignClients注解的类名。
PS: 后续就是spring的扫包流程,并且for循环将对应的bean装配到容器中。
对了,扫包完之后,registerFeignClients 后面,就是一个注册feignClient的流程。
2.2.2 registerFeignClients方法分析
这里的代码比较简单,都是做一些属性的装配。。
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
// 这行FeignClientFactoryBean需要去特殊注意
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);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
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 = contextId + "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;
}
// 注册到spring容器中去
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
在这个方法,我们需要重点关注的点是:
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
其实也就是 FeignClientFactoryBean 这个类。PS: 这个类实际上就是feign动态代理生成的核心类。
到这里,feign的包扫描就已经分析完了。
1.通过@EnableFeign注解,对feign的类进行一个标识,并且将其对应的属性进行容器的注入,交给spring来进行管理。
2. 重点关注,FeignClientFactoryBean这个类,这个类是Feign动态代理的关键类,Feign的动态代理就是通过它来进行完成。