基于 Spring Framework v5.2.6.RELEASE
概述
上一篇介绍了@
Configuration
注解标记的类别被 Spring 扫描到之后,是由 ConfigurationClassPostProcessor 来执行配置内容的处理的,在 Spring 上下文加载的过程中,会分别执行 ConfigurationClassPostProcessor 的postProcessBeanDefinitionRegistry
和postProcessBeanFactory
两个方法。本文来深入分析postProcessBeanDefinitionRegistry
方法的原理。
处理过程
我们从 ConfigurationClassPostProcessor 的postProcessBeanDefinitionRegistry
方法开始看起。
// org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistrypublicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistryregistry) { intregistryId=System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownewIllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against "+registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownewIllegalStateException( "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=newArrayList<>(); String[] candidateNames=registry.getBeanDefinitionNames();
方法的开头,定义了两个后面要用到的变量。
configCandidates
用来保存配置类的 BeanDefinitionHolder 对象,目前还是个空集合。candidateNames
是从容器中获取到的所有 BeanDefinition 的名字。
for (StringbeanName : candidateNames) { BeanDefinitionbeanDef=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); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(newBeanDefinitionHolder(beanDef, beanName)); } }
遍历candidateNames
中所有的 Bean 名称,并从容器中获取到对应的 BeanDefinition,对这个 BeanDefinition 进行判断。
如果它的ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE
属性值不是空,那么代表这个 BeanDefinition 已经作为配置类被处理过了,这个判断的作用从代码中记录日志信息中也可以看得出来。
如果没有被处理过,会通过 ConfigurationClassUtils 的方法checkConfigurationClassCandidate
对其进行校验,这一步是为了算选出符合条件的配置类,如果是,那么它将会被添加到configCandidates
中。
由此可知,这里的for
循环的作用,是将容器中所有还没有被处理过的配置类的 BeanDefinition 找出来。
这里,我们再进入checkConfigurationClassCandidate
方法的源码,深入分析筛选的逻辑。
这里的代码量也不少,我们慢慢分析。
StringclassName=beanDef.getBeanClassName(); if (className==null||beanDef.getFactoryMethodName() !=null) { returnfalse; }
首先获取 BeanDefinition 的className
,如果类型名称为空或者当前类已经制定了工厂方法,那么肯定不是配置类。
完成最初步的筛选后,接着往下看。
AnnotationMetadatametadata; if (beanDefinstanceofAnnotatedBeanDefinition&&className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) { // Can reuse the pre-parsed metadata from the given BeanDefinition...metadata= ((AnnotatedBeanDefinition) beanDef).getMetadata(); } elseif (beanDefinstanceofAbstractBeanDefinition&& ((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)) { returnfalse; } metadata=AnnotationMetadata.introspect(beanClass); } else { try { MetadataReadermetadataReader=metadataReaderFactory.getMetadataReader(className); metadata=metadataReader.getAnnotationMetadata(); } catch (IOExceptionex) { if (logger.isDebugEnabled()) { logger.debug("Could not find class file for introspecting configuration annotations: "+className, ex); } returnfalse; } }
这里主要是为了获取 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); } elseif (config!=null||isConfigurationCandidate(metadata)) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); } else { returnfalse; }
先从 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.Integerorder=getOrder(metadata); if (order!=null) { beanDef.setAttribute(ORDER_ATTRIBUTE, order); } returntrue;
最后,返回true
表示当前的 BeanDefinition 符合配置类的候选条件。
再次回到processConfigBeanDefinitions
方法中看后面的代码。
// Return immediately if no @Configuration classes were foundif (configCandidates.isEmpty()) { return; }
之后进行判断,如果没有找到符合条件的 BeanDefinition,则方法直接返回。
解析和处理配置类之前的准备工作
// Sort by previously determined @Order value, if applicableconfigCandidates.sort((bd1, bd2) -> { inti1=ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); inti2=ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); returnInteger.compare(i1, i2); });
在处理之前,还需要对configCandidates中所有的 BeanDefinition 进行排序,这里排序的一句就是上一部中给 BeanDefinition 添加的排序值,来自配置类的@
Order
注解。
接下来看下一步。
SingletonBeanRegistrysbr=null; if (registryinstanceofSingletonBeanRegistry) { sbr= (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGeneratorgenerator= (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator!=null) { this.componentScanBeanNameGenerator=generator; this.importBeanNameGenerator=generator; } } } if (this.environment==null) { this.environment=newStandardEnvironment(); }
首先判断了registry是不是 SingletonBeanRegistry 类型,registry是当前上下文的容器,从之前的代码分析中可以知道它的类型是 DefaultListableBeanFactory,它是 SingletonBeanRegistry 的实现类,因此这里if语句块的内容会被执行。
这里会判断当前的后处理器是否已经设置过了 Bean 名称生成器,如果没有的话,则从容器中获取,并赋值给当前的后处理器。
然后还会判断environment
是不是被初始化了,如果没有则进行初始化。
接着看后续的代码。
// Parse each @Configuration classConfigurationClassParserparser=newConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder>candidates=newLinkedHashSet<>(configCandidates); Set<ConfigurationClass>alreadyParsed=newHashSet<>(configCandidates.size());
在以上的代码块中,首先创建了一个 ConfigurationClassParser 对象,从名字可以看出它是配置类的解析器,用于后续解析配置类。
然后又创建了两个集合:
candidates
里存放了前面步骤中筛选出来的所有的配置类,这里虽然用了 Set 集合,但是 LinkedHashSet 是可以保证迭代顺序的,因此之前的排序信息不会受到影响。alreadyParsed
集合是一个存放 ConfigurationClass 类型对象的 Set 集合,从名字可以看出来它的作用已经解析过的配置类。
后续流程
至此,就完成了配置类的筛选和解析处理之前的准备工作,之后就是在一个do-while
循环里对candidates
里的配置类进行解析和处理。
do { // 配置类的解析和处理} while (!candidates.isEmpty());
限于本文的篇幅,解析和处理的流程放到下一篇来写。
总结
本文介绍了 ConfigurationClassPostProcessor 后处理器中的postProcessBeanDefinitionRegistry
方法处理配置类的过程的第一部分,即从所有的 BeanDefinition 中筛选出符合候选条件的配置类对应的 BeanDefinition,以及对这些配置类信息进行解析和处理前的准备工作。下一篇将继续通过源码深入分析解析和处理的原理。