Spring 源码阅读 36:postProcessBeanDefinitionRegistry 对 @Configuration 配置的解析和处理(1)
概述
上一篇介绍了@Configuration
注解标记的类别被 Spring 扫描到之后,是由 ConfigurationClassPostProcessor 来执行配置内容的处理的,在 Spring 上下文加载的过程中,会分别执行 ConfigurationClassPostProcessor 的postProcessBeanDefinitionRegistry
和postProcessBeanFactory
两个方法。本文来深入分析postProcessBeanDefinitionRegistry
方法的原理。
处理过程
我们从 ConfigurationClassPostProcessor 的postProcessBeanDefinitionRegistry
方法开始看起。
// org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
这个方法的逻辑比较好理解。
首先对当前执行的registry对象(其实就是当前上下文中的 BeanFactory 容器)获取一个registryId
,然后判断registriesPostProcessed
和factoriesPostProcessed
两个集合中是不是包含了这个registryId
。这里的目的是为了确保postProcessBeanDefinitionRegistry
和postProcessBeanFactory
两个方法不会被重复执行。
方法的最后,通过processConfigBeanDefinitions
来执行处理逻辑,我们来看这个方法的源码:
这个方法的代码量非常大,我们一部分一部分来分析。这里顺便提一句,在 Spring 的源码当中,类似这样包含复杂逻辑的方法,都会在方法体中用空行的方式来分割相对独立的小部分,并用注释说明每一小部分做了什么工作。接下来分析每一部分的时候,会把分析的那一部分代码都贴出来,方便查找。
筛选符合条件的配置类
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
方法的开头,定义了两个后面要用到的变量。
configCandidates
用来保存配置类的 BeanDefinitionHolder 对象,目前还是个空集合。candidateNames
是从容器中获取到的所有 BeanDefinition 的名字。
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
遍历candidateNames
中所有的 Bean 名称,并从容器中获取到对应的 BeanDefinition,对这个 BeanDefinition 进行判断。
如果它的ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE
属性值不是空,那么代表这个 BeanDefinition 已经作为配置类被处理过了,这个判断的作用从代码中记录日志信息中也可以看得出来。
如果没有被处理过,会通过 ConfigurationClassUtils 的方法checkConfigurationClassCandidate
对其进行校验,这一步是为了算选出符合条件的配置类,如果是,那么它将会被添加到configCandidates
中。
由此可知,这里的for
循环的作用,是将容器中所有还没有被处理过的配置类的 BeanDefinition 找出来。
这里,我们再进入checkConfigurationClassCandidate
方法的源码,深入分析筛选的逻辑。
这里的代码量也不少,我们慢慢分析。
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
首先获取 BeanDefinition 的className
,如果类型名称为空或者当前类已经制定了工厂方法,那么肯定不是配置类。
完成最初步的筛选后,接着往下看。
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = AnnotationMetadata.introspect(beanClass);
}
else {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " +
className, ex);
}
return false;
}
}
这里主要是为了获取 BeanDefinition 的 AnnotationMetadata 注解元数据,可以看到,不管当前的 BeanDefinition 是不是 AnnotatedBeanDefinition 类型的,都会尝试获取 AnnotationMetadata。获取到 AnnotationMetadata 就可以进行下一步的筛选。
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
先从 AnnotationMetadata 中获取@Configuration
注解的配置信息,并封装到一个名为config
的 Map 中。接下来进行判断:
- 如果config不为空,且
proxyBeanMethods
属性的值为true
(默认值),则给 BeanDefinition 设置CONFIGURATION_CLASS_ATTRIBUTE
属性的值为full
;
- 如果config不为空,且元数据符合配置类的候选条件,则给 BeanDefinition 设置
CONFIGURATION_CLASS_ATTRIBUTE
属性的值为lite
; - 如果以上两条都不符合,则当前的 BeanDefinition 不符合候选条件,返回
false
。
通过以上筛选之后,将配置类的@``Order
注解的值赋值给 BeanDefinition 的ORDER_ATTRIBUTE
对应的属性。
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
最后,返回true
表示当前的 BeanDefinition 符合配置类的候选条件。
再次回到processConfigBeanDefinitions
方法中看后面的代码。
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
之后进行判断,如果没有找到符合条件的 BeanDefinition,则方法直接返回。
解析和处理配置类之前的准备工作
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
在处理之前,还需要对configCandidates中所有的 BeanDefinition 进行排序,这里排序的一句就是上一部中给 BeanDefinition 添加的排序值,来自配置类的@``Order
注解。
接下来看下一步。
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
首先判断了registry是不是 SingletonBeanRegistry 类型,registry是当前上下文的容器,从之前的代码分析中可以知道它的类型是 DefaultListableBeanFactory,它是 SingletonBeanRegistry 的实现类,因此这里if语句块的内容会被执行。
这里会判断当前的后处理器是否已经设置过了 Bean 名称生成器,如果没有的话,则从容器中获取,并赋值给当前的后处理器。
然后还会判断environment
是不是被初始化了,如果没有则进行初始化。
接着看后续的代码。
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
在以上的代码块中,首先创建了一个 ConfigurationClassParser 对象,从名字可以看出它是配置类的解析器,用于后续解析配置类。
然后又创建了两个集合:
candidates
里存放了前面步骤中筛选出来的所有的配置类,这里虽然用了 Set 集合,但是 LinkedHashSet 是可以保证迭代顺序的,因此之前的排序信息不会受到影响。alreadyParsed
集合是一个存放 ConfigurationClass 类型对象的 Set 集合,从名字可以看出来它的作用已经解析过的配置类。
后续流程
至此,就完成了配置类的筛选和解析处理之前的准备工作,之后就是在一个do-while循环里对candidates里的配置类进行解析和处理。
do {
// 配置类的解析和处理
}
while (!candidates.isEmpty());
限于本文的篇幅,解析和处理的流程放到下一篇来写。
总结
本文介绍了 ConfigurationClassPostProcessor 后处理器中的postProcessBeanDefinitionRegistry
方法处理配置类的过程的第一部分,即从所有的 BeanDefinition 中筛选出符合候选条件的配置类对应的 BeanDefinition,以及对这些配置类信息进行解析和处理前的准备工作。下一篇将继续通过源码深入分析解析和处理的原理。
转载自:https://juejin.cn/post/7150652599442604062