本文已参与「新人创作礼」活动,一起开启掘金创作之路。
FeignClientsRegistrar#registerBeanDefinitions()
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 1. 注册FeignClientSpecification
registerDefaultConfiguration(metadata, registry);
// 2. 扫描@FeignClient注解
// 3. 注册FeignClient BeanDefinition
// 4. 通过FactoryBean 实例化Bean
registerFeignClients(metadata, registry);
}
- registerDefaultConfiguration()方法,注册FeignClientSpecification到注册表
- registerFeignClients()方法,对所有的包进行扫描,专门扫描@FeignClient注解修饰的接口
- registerFeignClient()方法,完成@FeignClient修饰的接口的注册
1. 注册FeignClientSpecification
启动OrderService服务,Feign扫描@FeignClient注解。
registerDefaultConfiguration()
AnnotationMetadata metadata是注解相关的一些元数据,BeanDefinitionRegistry registryBean实例注册器。
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
获取@EnableFeignClients注解的所有属性的值。
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
获取Aplication启动类的全限定名:default.center.leon.eurekaconsumerribbonfeignapi.EurekaConsumerRibbonFeignApiApplication
registerClientConfiguration()
注册 FeignClient 配置信息
BeanDefinitionBuilder,Bean定义的构造器,
启动类的全限定名,以及defaultConfiguration(空的),调用addConstructorArgValue,将name和defaultConfiguration作为入参构造BeanDefinition对象。
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
基于default.center.leon.eurekaconsumerribbonfeignapi.EurekaConsumerRibbonFeignApiApplication.FeignClientSpecification以及BeanDefinition,注册到BeeanDefinictionRegistry里面去。
- FeignClientSpecification:
default.center.leon.eurekaconsumerribbonfeignapi.EurekaConsumerRibbonFeignApiApplication。 - 解析@EnableFeignClients注解的attrs属性值
defaultConfiguration。 - 注册到BeanDefinitionRegistry中。
2. 扫描@FeignClient注解
获取scanner(ClassPathScanningCandidateComponentProvider),在classpath中扫描候选的组件的provider类。
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");
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
final Set<String> 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)));
}
处理要扫描的包路径,确定要扫描哪些包里面的@FeignClient注解标注的接口。
先是去获取了@EnableFeignClients里面的属性,因为这个属性里是可以配置basePackages,就是可以自己指定要扫描的包路径的
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
AnnotationTypeFilter,根据注解的类型进行过滤,用于过滤@FeignClient注解类型的过滤器
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
clients,在正常情况下,都是空的,因为我们一般不会在@EnableFeignClients注解中来配置clients属性,所以可以默认一般都是空的。
如果clients是空的,就会给组件扫描器添加一个注解过滤器,专门过滤@FeignClient注解的注解过滤器,就会加入组件扫描器中。
basePackages = getBasePackages(metadata),尝试从@EnableFeingClients注解中去获取要扫描的basePackages包路径,结果没有发现。在@EnableFeignClients注解没有配置basePackages属性的时候,就会去自动生成basePackages。
如果@EnableFefignClients中没有配置要扫描的包路径,默认是Application启动类所在的包。
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
使用组件扫描器,在启动类所在包中扫描包含@FeignClient注解的接口,封装成Set<BeanDefinition>返回。
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(
AnnotatedBeanDefinition beanDefinition) {
boolean isCandidate = false;
if (beanDefinition.getMetadata().isIndependent()) {
if (!beanDefinition.getMetadata().isAnnotation()) {
isCandidate = true;
}
}
return isCandidate;
}
};
}
创建组件扫描器时候重写isCandidateComponent()方法,在x@EnableFeignClients注解修饰类所在包下去扫描所有的类,每次扫到一个类,就交给这个isCandidateComponent()方法,判断是否是需要的类。
BeanDefinition candidateComponent是标注@FeignClient的接口,即center.leon.eurekaconsumerribbonfeignapi.product.server.ProductFacadeServiceFeign接口。
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
@FeignClient注解必须标注在接口上。
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
获取@FeignClient注解的属性,比如value、configuration属性。获取@FeignClient指定的要访问的服务名称。
registerClientConfiguration(registry, name, attributes.get("configuration"));
将name(服务名称) 及 configuration(配置类)注册到BeanDefinitionRegistry中。
registerFeignClient(registry, annotationMetadata, attributes);