远程调用组件中的动态注册BeanDefinition解析
前面讲到 FeignClientsRegistrar
的registerBeanDefinitions
函数主要做了两个事情,一是注册@EnableFeignClients
提供的自定义配置类中的相关Bean信息,二是根据@EnableFeignClients
提供的包信息扫描被@FeignClient
修饰的FeignCleint接口类,然后进行注册。
@EnableFeignClients
的自定义配置类就是被@Configuration
注解修饰的配置类,它会提供用来组装FeignClient
的各类组件的自定义实例。它们包括:Client
,Targeter
,Decoder
,Encoder
和Contract
。
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//获取到metadata中关于EnableFeignClients的属性值键值对。
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
//如果EnableFeignClients配置了defaultConfiguration类,那么才进行下一步操作,如果没有,会使用
//默认的FeignConfiguration
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"));
}
}
registerDefaultConfiguration
函数会判断@EnableFeignClients
的defaultConfiguration
属性是否被赋值。如果有,则将调用registerClientConfiguration
函数,进行BeanDefinitionRegistry
的注册。
BeanDefinitionRegistry
是Spring
框架中用于动态注册Bean信息的接口,调用其registerBeanDefinition
函数可以将BeanDefinition
注册到Spring容器中,name就是注册Bean的名称。
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
//使用BeanDefinitionBuilder来生成BeanDefinition并注册大registry上。
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
FeignClientSpecification
类实现了NamedContextFactory.Specification
接口,它是Feign组件示例化的重要一环,它会携带@EnableFeignClients
中的defaultConfiguration
值,供组件Bean初始化时使用。Spring Cloud
通过NamedContextFactory
来创建一些系列的子context,来让对应的Specification
在这些子context中创建bean对象。这样使得各个子context中的Bean相互独立,互不影响,可以方便的通过子context管理一系列的Bean实例。NamedContextFactory
有三个功能,一是创建AnnotationConfigApplicationContext
子context;二是在子context中获取Bean实例;三是当子context消亡时清除其中的Bean信息。在Feign
中,FeignContext
继承了NamedContextFactory
,是Feign
中组件实例化的最为关键的类。下图就是FeginContext
的相关类图。
FeignAutoConfiguration
是Feign
的自动配置类,在其中生成了FeignContext
实例。并且将之前注册的FeignClientSpecification
通过setConfigurations
函数设置给FeignContext
实例。这里就处理了默认配置类FeignClientsConfiguration
和自定义配置类的替换问题。如果FeignClientsRegistrar
没有注册自定义配置类,那么configurations
成员变量会是一个空队列。否则会在setConfigurations
中进行默认配置类的替换。
//FeignAutoConfiguration.java
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
//
context.setConfigurations(this.configurations);
return context;
}
//FeignContext.java
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
//将默认的FeignClientConfiguration作为参数传递给构造函数
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
}
//NamedContextFactory.java
public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType;
this.propertySourceName = propertySourceName;
this.propertyName = propertyName;
}
//对于FeignContext,List<C>就是指List<FeignClientSpecification>
public void setConfigurations(List<C> configurations) {
for (C client : configurations) {
this.configurations.put(client.getName(), client);
}
}
NamedContextFactory
的createContext
会创建具有名称的Spring
的AnnotationConfigApplicationContext
作为当前Context的child。这些AnnotationConfigApplicationContext
会被用来管理Feign
组件的不同实例。
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//获取该name所对应的configuration,如果有的话,就注册都子context中。
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
//注册default的Configuration,也就是FeignClientsRegistrar类的registerDefaultConfiguration函数中注册的Configuration
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
//注册PropertyPlaceholderAutoConfiguration和FeignClientsConfiguration配置类
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
//设置子context的Environment的propertySource属性源
//propertySourceName = feign; propertyName = feign.client.name
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.<String, Object> singletonMap(this.propertyName, name)));
//所有context的parent都相同,这样的话,一些相同的Bean可以通过parent context来获取。
if (this.parent != null) {
context.setParent(this.parent);
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
而由于NamedContextFactory
实现了DisposableBean
接口,当该bean消亡时,会清除掉自己创建的所有子context。
@Override
public void destroy() {
Collection<AnnotationConfigApplicationContext> values = this.contexts.values();
for (AnnotationConfigApplicationContext context : values) {
context.close();
}
this.contexts.clear();
}
NamedContextFactory
会创建出以name
作为唯一标识的AnnotationConfigApplicationContext
实例,然后每个AnnotationConfigApplicationContext
实例都会register一些配置类,从而可以给出一系列的基于配置类生成的Bean实例,这样,我们就可以基于name
来管理一系列的配置类Bean实例,从而可以为不同的FeignClient
准备不同配置Bean实例,比如说Decoder
,Encoder
之类的。这些我们会在后续的讲解中详细介绍。
FeignClientsRegistrar
做的第二件事情是扫描package并且注解被@FeignClient
修饰的接口类的bean信息。
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//1 生成自定义的ClassPathScanningProvider
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
//获取EnableFeignClients所有属性的键值对
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
//依照Annotation来进行TypeFilter,只会扫描出被FeignClient修饰的类
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
//2.1 如果没有设置clients属性,那么需要扫描basePackage,所以设置了AnnotationTypeFilter,并且去获取basePackage。
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
//2.2 如果设置了Clients属性,那么久不在进行basePackage的扫描,而是直接使用这些类所在的package
else {
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
//获取clients的值中的class信息,并将其所在package名称存储到basePackages中。
for (Class<?> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
//添加对这些类的Filter设置
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)));
}
//3 遍历上述过程中获取的basePackages列表
for (String basePackage : basePackages) {
//4.1 获取basepackage下的所有BeanDefinition
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
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");
//4.2 从这些BeanDefinition中获取到FeignClient的属性值
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
//4.3 对于单独某个FeignClient的configuration进行配置
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//4.4 注册FeignClient的BeanDefinition
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
registerFeignClients
函数就是依据@EnableFeignClients
的属性来获取basePackages
,然后获取这些包下的所有BeanDefinition
,获取修饰这些类的@FeignClient
的属性,来注册动态的Bean信息。
- 点赞
- 收藏
- 关注作者
评论(0)