spring之ConfigurationClassPostPro
前言
spring后置处理器哪家强?一定是ConfigurationClassPostProcessor
这个后置处理器
单从名字上看,是一个处理配置类的后置处理器
从功能上来讲,基本上所有用户定义的bean都是又ConfigurationClassPostProcessor加入到容器的
从角色角度来讲,用户实现BeanFactory后置处理器(包括很多第三方整合spring所实现的后置处理器),都是由ConfigurationClassPostProcessor负责注册到spring容器,才会在ApplicationContext执行后置处理器阶段被实例化并执行其后置处理方法
定位
如果把BeanFactory后置处理器比喻成鸡
,其bean定义比喻成蛋
,ConfigurationClassPostProcessor可以说是spring生命周期中的第一个鸡
,是一个创世纪的鸡
ConfigurationClassPostProcessor首先是一个BeanFactory后置处理器,而且是具有bean定义注册能力的BeanDefinitionRegistryPostProcessor
,刚说他是一个创世纪的后置处理器,那么他是从哪里来的呐?
实际上,它是在ApplicationContext初始化时加入到spring容器中,并在初始化后的invokeBeanFactoryPostProcessors
(执行后置处理器)被实例化并执行其后置处理方法,此时一般情况只有这一个唯一的BeanFactory后置处理器
在其后置处理方法执行过程,会注册用户定义的bean定义至容器,有可能会扫描并向容器中加入新的用户实现BeanFactory后置处理器,这些后置处理器也会在invokeBeanFactoryPostProcessors
阶段被陆续执行,如果这些后置处理器又注册了其它BeanFactory后置处理器,依然会在这个阶段被陆续执行,像是一个蛋生鸡,鸡生蛋的过程
![](https://img.haomeiwen.com/i9112801/6ce072fb5a316926.png)
关于这些问题,我再另一篇文章一文通透spring的初始化有详细介绍,本文重点分析ConfigurationClassPostProcessor的功能,这里只简单知道它的定位和执行时机即可
名词
再看源码之前,有些名词首先要理解
配置类
ConfigurationClassPostProcessor是一个处理配置类的后置处理器,那么什么样的类才是配置类?
初看这个名称以为是带有@Configuration
注解的类,事实也确实如此,但不仅限如此,配置类分两种
- full(完全版配置类)
即带有@Configuration
注解的配置类(忽略proxyBeanMethods特殊情况) - lite(精简版配置类)
没有@Configuration
注解,但有一些经常和该注解搭配的注解如@ComponentScan
,@Import
,@ImportResource
,@Bean
,这样的类相当于省略了@Configuration的精简版配置类,也会被视为配置类
更重要的带有@Component
的类也会被视为lite配置类,包括继承它的@Service
,@Controller
等
spring有一个工具方法用来判断某类的bean定义是否为配置类,即方法ConfigurationClassUtils.checkConfigurationClassCandidate
,该方法返回true就代表是配置类
![](https://img.haomeiwen.com/i9112801/4985252c223452c9.png)
parser(ConfigurationClassParser)
解析器,把配置类bean定义及其所有关联类的信息封装为ConfigurationClass
,这里的关联类主要指通过@ComponentScan指定的路径、@Import引入的类等进行关联,其中@ComponentScan扫描到的类会立即注册到bean容器
reader(ConfigurationClassBeanDefinitionReader)
读取器,主要承担读取parser解析出的ConfigurationClass
,将其它形式的关联类注册到bean定义容器,如@Import引入的类, @Bean方法创建的bean
parser 和 reader
ConfigurationClassParser有一段这样的注释:
This class helps separate the concern of parsing the structure of a Configuration class from the concern of registering BeanDefinition objects based on the content of that model (with the exception of {@ComponentScan} annotations which need to be registered immediately)
spring这分这两个类的本意是为了把配置类的解析
和注册bean定义
的逻辑分离开,但是@ComponentScan注解是个特例,它路径下的配置类会被立即注册到spring容器
说实话,不太理解spring为什么既然要拆分逻辑,但又拆分的不彻底,注释中所说的特例@ComponentScan实际上是最常用且数量最多的bean定义,所以大部分bean定义注册的活还是parser
承担的,而本应该肩负注册bean定义职责的reader
其实只注册了很少一部分的bean定义
如果我是设计者,我更希望parser
全权负责解析配置类至ConfigurationClass对象,reader
全权负责阅读ConfigurationClass对象并注册bean定义,这样应该更符合单一职责吧
源码解析
ConfigurationClassPostProcessor作为一个bean定义注册后置处理器,最重要的方法必然是postProcessBeanDefinitionRegistry
,那么接下来就看看在这个方法里它做了哪些事(以下代码省略一些支线逻辑)
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 省略一些判断...
processConfigBeanDefinitions(registry);
}
processConfigBeanDefinitions:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 候选配置类定义列表
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 获取容器中所有bean定义的名字
String[] candidateNames = registry.getBeanDefinitionNames();
// 循环名字
for (String beanName : candidateNames) {
// 获取bean定义
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
// 省略...
}
// 查看bean是否是ConfigurationClass
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 加入到候选配置类定义列表
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 没有@Configuration的bean就返回
if (configCandidates.isEmpty()) {
return;
}
// 省略排序,beanName生成规则,环境变量等...
// 构造一个配置类解析器,用来把bean定义的重要信息提取转化为ConfigurationClass
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());
do {
// 解析配置类
parser.parse(candidates);
parser.validate();
// 获取解析器中解析出的配置类ConfigurationClass:parser.getConfigurationClasses()
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 过滤掉已解析的配置类
configClasses.removeAll(alreadyParsed);
// 构造一个bean定义读取器
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 读取ConfigurationClass获取衍生bean定义并注册到容器
this.reader.loadBeanDefinitions(configClasses);
// 加入已解析配置类
alreadyParsed.addAll(configClasses);
// 清空候选配置类定义列表
candidates.clear();
// 如果容器中bean定义有新增
if (registry.getBeanDefinitionCount() > candidateNames.length) {
/** 查找出新增的配置类bean定义 start **/
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
/**查找出新增的配置类bean定义 end , 最终新的配置bean定义加入到候选配置类定义列表 **/
// 覆盖所有beanName,为下一次判断是否有新增
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty()); // 只要候候选配置类定义列表不为空就一直循环下去
// 省略...
}
上面的代码比较长,但流程很清晰,总结一下
- 查找spring容器中的配置类bean定义,存储
候选列表
- 使用解析器,解析
候选列表
中的配置类bean定义,根据配置的扫描范围把相关的类都解析为ConfigurationClass对象(其中@CommpentScan下扫描的类直接注册bean定义) - 使用读取器,读取上一步解析出的新未解析ConfigurationClass,注册相关bean定义
- 清空
候选列表
- 继续查询spring容器,如果有新增的配置类bean定义,读取加入
候选列表
,跳转回第2步
画个图清晰一下
![](https://img.haomeiwen.com/i9112801/2aa264d09135eab1.png)
为什么第5步会有新增呐?是因为第2/3步是有可能注册新的配置类bean定义至容器,只想说spring太喜欢玩这种把戏了
通过代码分析也可以得知,ConfigurationClassPostProcessor重点使用了两大利器:parser(解析器) 和 reader(读取器),接下来就具体分析二者都干了什么活
Parser
上文ConfigurationClassPostProcessor对parser具体使用主要有三处
- 初始化构造parser,传入了BeanDefinitionRegistry等构造传参数
- 调用parse方法解析bean定义集合
- 通过getConfigurationClasses方法获取解析结果configurationClasses
![](https://img.haomeiwen.com/i9112801/149ce3757dab6557.png)
configurationClasses
ConfigurationClassParser中一个属性configurationClasses,用来存放解析出来的结果
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();
对应的get方法,就是ConfigurationClassPostProcessor最终获取解析结果的方法
public Set<ConfigurationClass> getConfigurationClasses() {
return this.configurationClasses.keySet();
}
解析(parse)的重点责任就是根据传入的参数(bean定义列表)转化为Map<ConfigurationClass, ConfigurationClass>,下面重点分析分析方法:parse
方法:
parse
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 循环
for (BeanDefinitionHolder holder : configCandidates) {
// 获取bean定义
BeanDefinition bd = holder.getBeanDefinition();
try {
// 根据bean定义的类型走不通参数的重载parse方法
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
// 省略catch...
}
this.deferredImportSelectorHandler.process();
}
因为是多bean定义参数,所以这里主要是循环再根据不同类型走重载的parse方法,这些重载方法主要是适配各种情况,最终都会根据bean定义生成一个ConfigurationClass
对象传入processConfigurationClass
方法
![](https://img.haomeiwen.com/i9112801/be028d84401b9bc5.png)
注这里最后有一步this.deferredImportSelectorHandler.process()
,先注意一下后面会说到
processConfigurationClass
processConfigurationClass这个方法贴主要代码
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
//
this.configurationClasses.put(configClass, configClass);
又是一个循环,调用doProcessConfigurationClass
方法,如果有返回值,递归调用doProcessConfigurationClass,看spring的注释说递归解析配置类和他的父类,所以这代码的意思就是解析配置类,如果有父类再解析父类,如果父类有父类再一直解析下去。
最后this.configurationClasses.put(configClass, configClass);
就是把上一步根据bean定义生成的ConfigurationClass对象存入解析的结果map:configurationClasses,实际上,对解析结果的添加只有这一处代码
如果单单看到这里,parser的功能就是把传入的多个bean定义循环转换为ConfigurationClass并存入解析结果中,相当于传入多少bean定义就转换多少ConfigurationClass对象
单parse方法的功能远不止如此,重点在doProcessConfigurationClass
方法
doProcessConfigurationClass
重点来到了doProcessConfigurationClass
方法:
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 1.内部类 这一步看看有没有内部类
processMemberClasses(configClass, sourceClass, filter);
}
// 2.@PropertySource 这一步解析@PropertySource注解,更改配置文件位置时会使用,一般使用默认位置,不咋更改
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
// 省略...
}
// 3.@ComponentScan 这一步就很重要了,解析@ComponentScan注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 开始扫描,把@ComponentScan指定包下的@Component类全部扫描出来,并且会注册bean定义至容器
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 把所有扫描到的beanClass递归解析
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
// 获取bean定义
bdCand = holder.getBeanDefinition();
}
//判断是不是配置类
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 解析
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 4.@Import 解析@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 5.@ImportResource解析@ImportResource解析
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 6.@Bean 解析带有@Bean注解的方法,加入到configClass的beanMethods属性中
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 6.接口@Bean 解析实现的接口中带有@Bean注解的默认方法,加入到configClass的beanMethods属性中
processInterfaces(configClass, sourceClass);
// 7.父类 如果有父类返回父类,以继续解析
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// 没有父类,解析结束
return null;
}
这个方法很长,主要包含以下几个功能
- 解析内部类
- 解析@PropertySource注解
- 解析@ComponentScan注解,扫描指定包下的所有@Component类,直接注册bean定义至spring,并递归解析
- 解析@Import注解
- 解析@ImportResource注解
- 解析@Bean
- 解析实现接口中的@Bean
- 返回父类
所以doProcessConfigurationClass这一步的重点工作就是扫描某个配置类的注解,根据注解功能找到指定扫描范围的类,递归
解析(调用parse或processConfigurationClass方法)
比如@ComponentScan就是扫描到basePackages指定路径下的类,然后通过递归调用parse解析这些类,@Import通过value属性指定的类,递归调用processConfigurationClass解析
重点这个递归
,有了它就可以实现一个bean定义解析出多个ConfigurationClass对象
这里有个特例,@ComponentScan扫描的类会被直接注册到spring容器
接下来再把这几个注解的解析过程详细介绍一下
解析内部类
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 1.内部类 这一步看看有没有内部类,一般不咋用
processMemberClasses(configClass, sourceClass, filter);
}
也就是说如果一个类有@Component注解,processMemberClasses内部会解析当前类的内部类,如果是配置类,则会递归
调用processConfigurationClass去解析这个内部类
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {
// 获取所有内部类
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
// 候选列表
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
// 如果是配置类,加入候选
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
OrderComparator.sort(candidates);
// 循环候选列表
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
// 递归解析内部类
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
this.importStack.pop();
}
}
}
}
}
这就是为什么内部类也能加入bean容器中
解析@PropertySource注解
这个真没用过,主要是自定义配置文件,我觉得配置文件放在spring约定的地就不错,这个就不研究了
解析@ComponentScan
这个都懂,就是扫描的包路径,这一步的代码细分析下
(1) 扫描包下的类
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
这个componentScanParser
内部有个scaner(扫描器),扫描@Component
注解的类,包括子注解@Configuration
,@Service
等(这过程还会直接把扫描到的类注册bean定义)
![](https://img.haomeiwen.com/i9112801/7b528779a86a0533.png)
(2) 循环判断扫描到的类是否是ConfigurationClass,如果是则递归
解析
// 如果是ConfigurationClass
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 解析
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
解析@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
这个也比较重要,参数getImports
方法获取到了当前配置类所有@Import注解指定的类集合,然后进入processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 省略...
// 循环所有@Import指定的类
for (SourceClass candidate : importCandidates) {
// 如果这个类实现了ImportSelector,即导入多个类
if (candidate.isAssignable(ImportSelector.class)) {
// 实例化这个导入的ImportSelector类
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
// 省略一些过滤...
// 如果是延迟导入,交给deferredImportSelectorHandler处理
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
// 普通的多个导入
else {
// 找到导入的多个类
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归调用当前方法
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// 如果这个类实现了ImportBeanDefinitionRegistrar
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 实例化这个ImportBeanDefinitionRegistrar类
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 加入该类到当前配置类解析对象ConfigurationClass的importBeanDefinitionRegistrars列表属性中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 排除上面两种情况,这个类就是个普通类
else {
// 省略...
// 递归解析这个类
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
这里涉及的内容较多,主要分几种情况
-
如果导入的就是普通类
直接递归
解析这个类 -
如果导入的类实现了ImportBeanDefinitionRegistrar(导入的bean定义注册器)
直接实例化这个类,并加入到当前解析配置类的importBeanDefinitionRegistrars(列表)属性中,以便后续调用其registerBeanDefinitions
方法 -
如果导入的是实现了ImportSelector的普通类(多类导入)
直接实例化这个类,循环selectImports
方法放回的多个类,递归调用processImports -
如果导入的是实现了DeferredImportSelector的特殊ImportSelector类(延迟多类导入)
直接实例化这个类,委托给解析器内部的deferredImportSelectorHandler处理,deferredImportSelectorHandler内部把这个实例对象和当前解析类封装为DeferredImportSelectorHolder, 暂存入解析器内部的deferredImportSelectors属性
因为是延迟,所以这里暂存了一下,并没有像普通ImportSelector那样循环递归解析processImports,而其真正的执行实际就是上文parse主方法最后的那一句this.deferredImportSelectorHandler.process()
,而process方法内部依然是循环递归processImports方法,只不过延迟了,相当于所有的类被解析过才开始解析DeferredImportSelector导入的类
解析@ImportResource注解
主要为了兼容之前的xml写法,不做研究
解析@Bean
@Bean注解一般经常使用,使用工厂方法创建一个bean,一般就是@Configuration+@Bean,当然@Component+@Bean也可以
// 获取所有@Bean注解方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
// 包装成BeanMethod加入到配置类beanMethods属性中
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
这里通过解析当前配置类@Bean方法,并将方法打包为BeanMethod存入配置类beanMethods属性中
解析实现接口中的@Bean
这是对@Bean注解的一个扩展,解析配置类实现的接口中带有@Bean注解的默认方法,即某个配置类实现的接口有@Bean注解也会和普通的@Bean方法一样被加入到配置类beanMethods属性中
返回父类
最后一步如果返回父类继续递归解析,这个不做过多介绍了
总结Parser
基本上parser内部解析的重点方法都分析完了,总结一下,parser都干了什么?
-
parser作为一个解析器,根据传入的配置类解析出多个
ConfigurationClass
对象 -
配置类实际上是一个树形结构,他的子节点包含@ComponentScan扫描到的配置类,@Import引入的普通类等
-
而spring中这个树结构的根节点一般就是用户定义的主启动类
-
parser通过递归解析完成整个树的解析(解析成多个ConfigurationClass对象),并把解析结果存入parser内部
configurationClasses
属性 -
parser解析某个配置类节点过程中
会把其@Bean方法加入ConfigurationClass对象的属性beanMethods
列表中
会把@Import注解引入的ImportBeanDefinitionRegistrar加入ConfigurationClass对象的属性importBeanDefinitionRegistrars
列表中
会把@Import注解引入的DeferredImportSelector加入到解析器内部的属性deferredImportSelectors
列表中,延迟到所有节点解析完成后再统一解析 -
在解析某配置类的@ComponentScan注解时,扫描到的类会同时注册bean定义至spring容器
所以最终结果是把根配置类下的整个树扫描,生成多个ConfigurationClass
对象,同时@ComponentScan扫描到的配置类会直接注册bean定义
最后画个图梳理整个解析过程
![](https://img.haomeiwen.com/i9112801/1549497c89559b08.png)
DeferredImportSelector只不过是延迟导入,从整个解析过程来看最终结果与@Import一样
Reader
相比于parser,reader的任务轻松多了,主要任务就是把parser解析出的ConfigurationClass
集合读取并转换为bean定义,并最终注册到spring容器
reader.loadBeanDefinitions:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
// 循环ConfigurationClass集合
for (ConfigurationClass configClass : configurationModel) {
// 读取ConfigurationClass并注册bean定义
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
但是上面parser已经把@ComponentScan扫描到的配置类注册完了,reader注册什么呐,看一下loadBeanDefinitionsForConfigurationClass
代码是如何注册每个ConfigurationClass对象的
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 省略的
// 注册被Import引入的类,标识ConfigurationClass.isImported==true
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 注册@Bean的工厂方法,存储在ConfigurationClass.beanMethods属性中
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// ImportedResource(忽略)
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 执行ImportBeanDefinitionRegistrar实现注册用户自定义bean定义,存储在ConfigurationClass.importBeanDefinitionRegistrars属性中
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
Parser注册了@ComponentScan
扫描的bean定义,而Reader主要注册了这几个bean定义
- 通过@Import引入的(标识ConfigurationClass.isImported==true)
- @Bean的工厂方法(存储在ConfigurationClass.beanMethods属性中)
- 执行ImportBeanDefinitionRegistrar实现注册用户自定义bean定义(存储在ConfigurationClass.importBeanDefinitionRegistrars属性中)
这么一看Reader反而成了捡漏的了...,就是把Parser没注册的bean定义都注册上
其中loadBeanDefinitionsFromRegistrars这步就是执行ImportBeanDefinitionRegistrar实现的registerBeanDefinitions
方法,即用户自定义注册bean常用的@Import+ImportBeanDefinitionRegistrar模式的实际执行时机
![](https://img.haomeiwen.com/i9112801/614d28dd28831b4f.png)
总结
本文完整的示意图如下
![](https://img.haomeiwen.com/i9112801/131f8b14c758d13b.png)
以上就是ConfigurationClassPostProcessor源码的详解,可能有错过的点,以后遇到再补充