@EnableFeignClients
注解
java
@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
java
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
}
可以看到该类实现了ImportBeanDefinitionRegistrar
接口
ImportBeanDefinitionRegistrar
接口允许开发者在运行时动态地向 Spring 应用程序上下文中注册 Bean 定义,这个接口通常与 @Import
注解结合使用,当 Spring 容器扫描到带有 @Import
注解的类时,会调用实现了ImportBeanDefinitionRegistrar
接口的类registerBeanDefinitions
方法。
开发者可以编写自定义逻辑来创建BeanDefinition
对象(这些对象描述了如何创建Bean实例),并使用BeanDefinitionRegistry
将它们注册到Spring容器中
FeignClientsRegistrar#registerBeanDefinitions()
java
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 处理@EnableFeignClients注解上的属性配置,将配置注册到容器中
registerDefaultConfiguration(metadata, registry);
// 注册@FeignClient对应的接口,加入到容器中
registerFeignClients(metadata, registry);
}
FeignClientsRegistrar#registerFeignClients()
java
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
//获取@EnableFeignClients的属性集合
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
//获取clients属性
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
//拿到spring内置的扫描器
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
//添加过滤器:过滤器所有被 @FeignClient 标记的接口
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
//获取包的扫描路径
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
//扫描并添加到集合中
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
//循环处理@FeignClient接口
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
//获取@FeignClient注解的属性集合
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
//获取client的名称
String name = getClientName(attributes);
registerClientConfiguration(registry, name, attributes.get("configuration"));
//核心方法,注入FeignClient对象
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
第一部分是扫描@Feignclient
注解的接口类,第二部分就是对扫描出的接口类进行处理,主要关注registerClientConfiguration
和registerFeignClient
函数。
其中registerClientConfiguration
是为了处理@FeignClient#configuration
属性的,在这个函数会往Spring容器中添加#{serviceName}.FeignClientSpecification
作为名字的FeignClientSpecification
类对象
而registerFeignClient
函数则是将第一步扫描出来的接口注入到Spring容器中。
接下来我们看一下registerFeignClient
函数
java
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
//创建FeignClientFactoryBean对象,下一篇会重点分析FeignClientFactoryBean
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
factoryBean.setRefreshableClient(isClientRefreshEnabled());
//通过BeanDefinitionBuilder创建对象
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
//设置feign接口参数,包括访问路径、fallback等
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
}
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
//获取BeanDefinition对象
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[] { contextId + "FeignClient" };
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
//调用registerBeanDefinition方法注入Spring容器中
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
registerOptionsBeanDefinition(registry, contextId);
}
看一下registerBeanDefinition
方法
java
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
//通过Spring提供的注册器将FeignClient接口包装成的BeanDefinition注入到容器中
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
总结
通过@EnableFeignClients
中的@Import
和FeignClientsRegistrar
配置,当Spring容器扫描到带有 @Import
注解的类时,会调用实现了ImportBeanDefinitionRegistrar
接口的类registerBeanDefinitions
方法。从而扫描配置包下面的所有@FeigntClient
注解的接口,包装成BeanDefinition
并注入到Spring容器中。