概述
本文以SpringBoot应用为基础,尝试分析基于注解@Configuration
的配置类是如何向Spring容器注册BeanDefinition的过程
其中主要分析了 ConfigurationClassPostProcessor
这个BeanDefinitionRegistryPostProcessor
即Bean定义注册后置处理器,在Spring启动过程中对@Configuration配置类的处理,主要体现在 解析并发现所有配置类,处理配置类的相关逻辑(如配置类上的@ComponentScan、@Import、@Bean注解等),注册其中的BeanDefinition
SpringBoot版本:2.0.9.RELEASE
Spring版本:5.0.13.RELEASE
ConfigurationClassPostProcessor如何被引入
首先看一下ConfigurationClassPostProcessor的类继承关系
从红框中可以看出ConfigurationClassPostProcessor
是BeanDefinitionRegistryPostProcessor
接口的实现类,即是一个Bean定义注册的后置处理器,会在Spring容器启动时被调用,具体时机为
// 调用链
AbstractApplicationContext.refresh()
=> invokeBeanFactoryPostProcessors()
=> PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()
invokeBeanFactoryPostProcessors()
会先调用所有的BeanDefinitionRegistryPostProcessor
之后,再调用所有的BeanFactoryPostProcessor
ConfigurationClassPostProcessor又是如何被引入Spring的呢??
SpringBoot应用会在ApplicationContext应用上下文被创建的构造函数中new AnnotatedBeanDefinitionReader
这个用于注册基于注解的BeanDefinition的Reader,在其构造中又会调用AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)
使用工具类向Spring容器中注册一些所谓的注解配置处理器,其中就包含ConfigurationClassPostProcessor
// ConfigurationClassPostProcessor被注册
AnnotationConfigServletWebServerApplicationContext构造
=> new AnnotatedBeanDefinitionReader(registry)
=> AnnotationConfigUtils.registerAnnotationConfigProcessors(registry)
=> 注册ConfigurationClassPostProcessor到Spring容器
ConfigurationClassPostProcessor处理过程简述
首先,ConfigurationClassPostProcessor后置处理器的处理入口为postProcessBeanDefinitionRegistry()
方法。其主要使用了ConfigurationClassParser
配置类解析器解析@Configuration
配置类上的诸如@ComponentScan
、@Import
、@Bean
等注解,并尝试发现所有的配置类;还使用了ConfigurationClassBeanDefinitionReader
注册所发现的所有配置类中的所有Bean定义;结束执行的条件是所有配置类都被发现和处理,相应的bean定义注册到容器
大致流程如下:
1、通过BeanDefinitionRegistry查找当前Spring容器中所有BeanDefinition
2、通过ConfigurationClassUtils.checkConfigurationClassCandidate()
检查BeanDefinition是否为 “完全配置类” 或 “简化配置类”,并对配置类做标记,放入集合待后续处理
Spring配置类的分类可以 参考
3、通过 ConfigurationClassParser解析器
parse解析配置类集合,尝试通过它们找到其它配置类
4、使用 ConfigurationClassBeanDefinitionReader
注册通过所发现的配置类中找到的所有beanDefinition
5、处理完一轮配置类后,查看BeanDefinitionRegistry中是否存在新加载的且还未被处理过的 “完全配置类” 或 “简化配置类”,有的话继续上面步骤
其中第3、4步后面重点分析
ConfigurationClassParser#parse():解析构建配置类
对于SpringBoot应用来说,参与解析的种子配置文件即为SpringBoot的Application启动类
解析构建配置类流程
通过ConfigurationClassParser解析器parse解析配置类集合,尝试通过它们找到其它配置类
循环解析所有配置类 ConfigurationClassParser#processConfigurationClass()
根据@Conditional的ConfigurationPhase.PARSE_CONFIGURATION阶段条件判断是否跳过配置类
注意:有些@Conditional是在当前这个PARSE_CONFIGURATION解析配置阶段使用的,有些是在REGISTER_BEAN注册beanDefinition阶段使用的
【重点】调用ConfigurationClassParser#doProcessConfigurationClass()循环解析配置类,直到不