@EnableFeignClients注解源码解析
转载请注明出处:
@EnableFeignClients 注解定义的源码
@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 {};
}
这个注解通过@Import注解导入一个配置类FeignClientsRegistrar.class ;FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,所以Spring Boot在启动的时候,会去调用FeignClientsRegistrar类中的registerBeanDefinitions来动态往spring容器中注入bean。
接下来看一下registerBeanDefinitions的实现
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry)
//这个方式是注入一些配置,就是对EnableFeignClients注解属性的解析,比如bean 名称等
registerDefaultConfiguration(metadata, registry);
//这个方法是扫描加了@FeignClient注解
registerFeignClients(metadata, registry);
}
查看 registerFeignClients 方法的实现:
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
scanner.setResourceLoader(this.resourceLoader);
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
Object basePackages;
if (clients != null && clients.length != 0) {
final Set<String> clientClasses = new HashSet();
basePackages = new HashSet();
Class[] var9 = clients;
int var10 = clients.length;
for(int var11 = 0; var11 < var10; ++var11) {
Class<?> clazz = var9[var11];
((Set)basePackages).add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(new FeignClientsRegistrar.AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
} else {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = this.getBasePackages(metadata);
}
Iterator var17 = ((Set)basePackages).iterator();
while(var17.hasNext()) {
String basePackage = (String)var17.next();
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
Iterator var21 = candidateComponents.iterator();
while(var21.hasNext()) {
BeanDefinition candidateComponent = (BeanDefinition)var21.next();
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = this.getClientName(attributes);
// 每个Feign的客户端的配置类封装成一个FeignClientSpecification的BeanDefinition,注册到spring容器中
this.registerClientConfiguration(registry, name, attributes.get("configuration"));
// 重新构造了一个BeanDefinition,这个BeanDefinition的指定的class类型是FeignClientFactoryBean,spring在生成bean的时候,判断BeanDefinition中bean的class如果是FactoryBean的实现的话,会调用这个实现类的getObject来获取对象
this.registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
然后就遍历每个目录,找到每个标有@FeignClient注解的类,对每个类就生成一个BeanDefinition,可以把BeanDefinition看成对每个标有@FeignClient注解的类信息的封装。
拿到一堆BeanDefinition之后,会遍历BeanDefinition,然后调用registerClientConfiguration和registerFeignClient方法。
@EnableFeignClinets的作用就说完了。这个类的主要作用是扫描指定(不指定就默认路径下的)所有加了@FeignClient注解的类,然后每个类都会生成一个BeanDefinition,随后遍历每个BeanDefinition,然后取出每个@FeignClient注解的属性,构造新的BeanDefinition,传入FeignClientFactoryBean的class,随后注入到spring容器中;同时有配置类的也会将配置类构件出一个bean class为FeignClientSpecification的BeanDefinition注入到spring容器中。
如果想了解feign 如何进行rpc 调用以及调用时如何实现负载均衡可以看这篇文章:
Feign 进行rpc 调用时使用ribbon负载均衡源码解析