文章目录
1. 前言
在阅读本文之前,可以先思考一下以下几个问题:
- (1) @Configuration注解的作用是什么,Spring是如何解析加了@Configuration注解的类?
- (2) Spring在什么时候对@ComponentScan、@ComponentScans注解进行了解析?
- (3) Spring什么时候解析了@Import注解,如何解析的?
- (4) Spring什么时候解析了@Bean注解?
其实上面的四个问题,都可以一个类来解释,即本文的主角: ConfigurationClassPostProcessor
。
那么这个类究竟是如何来解决这些问题的呢?
2. ConfigurationClassPostProcessor
ConfigurationClassPostProcessor
是一个BeanFactory的后置处理器
,因此它的主要功能是参与BeanFactory的建造,在这个类中,会解析加了@Configuration的配置类,还会解析@ComponentScan注解(并根据@ComponentScans注解扫描指定路径下的类,此处会递归解析,因为被解析出的类可能有@ComponentScans注解或@import注解),以及解析@Import等注解。
实现
BeanFactoryPostProcessor
接口的称之为后置处理器。
-
ConfigurationClassPostProcessor 实现了
BeanDefinitionRegistryPostProcessor
接口,而 BeanDefinitionRegistryPostProcessor 接口继承了BeanFactoryPostProcessor
接口,所以 ConfigurationClassPostProcessor 中需要重写 postProcessBeanDefinitionRegistry() 方法和 postProcessBeanFactory() 方法。而ConfigurationClassPostProcessor类的作用就是通过这两个方法去实现的。
-
ConfigurationClassPostProcessor这个类是Spring内置的一个BeanFactory后置处理器,是在
this()方法
中将其添加到BeanDefinitionMap
中的(可以参考笔者的另一篇文章 《Spring源码系列之容器启动流程》 )。在执行过程中,会先执行postProcessBeanDefinitionRegistry(),然后执行postProcessBeanFactory()。
也可以参见参见《spring源码》中<1.1.1.2 实例化建BeanDefinition读取器: AnnotatedBeanDefinitionReader>章节提到的知识,利用AnnotatedBeanDefinitionReader注册的内置的几个bean,其中的bean的名称org.springframework.context.annotation.internalConfigurationAnnotationProcessor,class=“ConfigurationClassPostProcessor”
3. 执行流程
3.1 postProcessBeanDefinitionRegistry()
该方法是定义在BeanDefinitionRegistryPostProcessor中的抽象方法,会优先执行。
该方法的作用是根据已经注册的BeanDefinition中找到配置类,配置类有2种,lite和full,对配置类进行解析!
postProcessBeanDefinitionRegistry()方法中调用了processConfigBeanDefinitions()
,所以核心逻辑在processConfigBeanDefinition()方法中:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){
...省略了不重要的代码
processConfigBeanDefinitions(registry);
}
processConfigBeanDefinitions()方法代码如下(省略了部分不重要的代码),源码中添加了许多注释,解释了部分重要方法的作用:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//待处理的配置类集合
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
//[1]提取未处理过的配置类
//[1.1]获得所有已经注册的BeanDefinition的Name集合,放入candidateNames数组
String[] candidateNames = registry.getBeanDefinitionNames();
//[1.2]循环candidateNames数组
for (String beanName : candidateNames) {
//[1.3]根据beanName获得BeanDefinition
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//[1.4]配置类有2种,full或lite
//当我们注册配置类的时候,可以不加Configuration注解,直接使用Component ComponentScan Import ImportResource注解,称之为Lite配置类
//如果加了@Configuration注解,就称之为Full配置类
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
//[1.5] 如果BeanDefinition中的configurationClass属性已经为full或者lite,则意味着已经处理过了,直接跳过
// 也就是说某个配置类从未被处理时,configurationClass属性一定不是full或lite,处理后,会设置full或lite
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//[1.6]checkConfigurationClassCandidate()会判断一个是否是一个配置类,并为BeanDefinition设置属性为lite或者full。
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//[1.7]若是一个配置类,则加入待处理的集合
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 如果没有配置类(full或lite都是配置类),直接返回
if (configCandidates.isEmpty()) {
return;
}
//如果加了@Order注解,进行排序,主要是存在多个配置类时需要定优先级
Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
@Override
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
}
});
SingletonBeanRegistry singletonRegistry = null;
//[2]处理自定义beanName生成器
//[2.1] DefaultListableBeanFactory最终会实现SingletonBeanRegistry接口,所以可以进入到这个if
if (registry instanceof SingletonBeanRegistry) {
singletonRegistry = (SingletonBeanRegistry) registry;
//[1.2]spring中可以修改默认的bean命名方式,这里就是看用户有没有自定义bean命名方式(生成器),虽然一般没有人会这么做;
//一般用默认的,有自定义的则覆盖之
//就是判断是否存在org.springframework.context.annotation.internalConfigurationBeanNameGenerator这个类的实例
if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
//[3]扫描配置类中@ComponentScan路径并注册BeanDefinition
//[3.1]实例化ConfigurationClassParser作为解析器, 作用是解析各个配置类(带@Configuration注解的类)
//注意:registry,即当前核心注册器作为入参,解析出来的类也会被注册到registry的beanDefinitionMap中
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
//[3.2] candidates用于存储待处理的配置类集合
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
//[3.3] alreadyParsed用于存储已经处理的,作判断条件依据,避免重复处理
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
//[3.4]解析配置类(MainConfig.java)
//(在@ComponentScan定义路径下被扫描出的类,带@Import、@Bean标签的类
parser.parse(candidates);
parser.validate();
//[3.5]获取在扫描时put进去的configurationClasses,这里是Car、MyBeanFactoryPostProcessor、MyBeanPostProcessor、mainConfig
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// [3.6]创建能注册beanDefinition处理器
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//[3.7]注册被扫描出的类为beanDefinition
//实际上经过上一步的parse()后,解析出来的bean已经放入到BeanDefinition中了,但是由于这些bean可能会引入新的bean,
//例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean,或者bean中存在被@Bean注解的方法
//因此需要执行一次loadBeanDefinition(),这样就会执行ImportBeanDefinitionRegistrar或者ImportSelector接口的方法或者@Bean注释的方法
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
//[3.8]查看是否有新的未解析的配置类,如果有,则解析将其解析
//[3.8.1]实际上经过[3.4]和[3.7]这2步处理后,解析出来的类也会被注册到beanDefinitionMap中,count值会增加
//candidateNames集合是我们每次执行do while中的[3.4]和[3.7]步骤之前的beanDefinitionMap的快照
//如果二者不同,说明存在新的类,这些类可能是新的配置类,需要我们解析
if (registry.getBeanDefinitionCount() > candidateNames.length) {
//[3.8.2]存储新发现的待解析的类
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<String>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
//[3.8.3] 如果有未解析的类,则将其添加到candidates中,这样candidates不为空,就会进入到下一次的while的循环中
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) {
if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
-
提取提取未处理过的配置类,为解析做准备
1.1 获得所有的已注册的BeanName,放入candidateNames数组。
1.2 循环candidateNames数组
1.3 根据beanName获得BeanDefinition
1.4 判断此BeanDefinition是否是已经被处理过的配置类,如果已经处理过了就跳过。判断的依据是BeanDefinition 的configurationClass属性
1.6 进入该分支,说明不是已经被处理过的配置类,有2种可能:
- 本身压根不是配置类,跳过;
- 是未处理过的配置类,加入集合,待后续处理。注意此处需要标记一下full或lite,避免下次重复处理
知识点:如何判断配置类属于Full配置类,还是Lite配置类
当我们注册配置类的时候,可以不加@Configuration注解,直接使用@Component @ComponentScan、 @Import 、@ImportResource等注解,Spring把这种配置类称之为Lite配置类, 如果加了@Configuration注解,就称之为Full配置类。
如果我们注册了Lite配置类,我们getBean这个配置类,会发现它就是原本的那个配置类,如果我们注册了Full配置类,我们getBean这个配置类,会发现它已经不是原本那个配置类了,而是已经被cgilb代理的类了。
- 处理自定义beanName生成器,一般不会自定义。
- 解析配置类,这个步骤是核心
[3.1]实例化ConfigurationClassParser作为解析器, 作用是解析各个配置类
注意:registry,即当前核心注册器作为入参,解析出来的类也会被注册到该registry的beanDefinitionMap中
[3.4]解析配置类,调用ConfigurationClassParser 的parse()
[3.6][3.7]用ConfigurationClassBeanDefinitionReader解析ImportBeanDefinitionRegistrar或者ImportSelector接口的bean
[3.8]查看是否有新的未解析的类,如果有,则解析将其解析
3.1.1 [1.6]ConfigurationClassUtils.checkConfigurationClassCandidate()
该方法是用来判断一个是否是一个配置类
,并为BeanDefinition设置configurationClass
属性为lite
或者full
,一旦设置后,标记该配置类被处理过了,避免重复处理。
如果加了@Configuration,那么对应的BeanDefinition为full,如果加了@Bean,@Component,@ComponentScan,@Import,@ImportResource这些注解,则为lite。lite和full均表示这个BeanDefinition对应的类是一个配置类。
部分代码如下:
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
// ... 省略部分不重要的代码
if (isFullConfigurationCandidate(metadata)) {
// 含有@Configuration注解,那么对应的BeanDefinition的configurationClass属性值设置为full
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (isLiteConfigurationCandidate(metadata)) {
// 含有@Bean,@Component,@ComponentScan,@Import,@ImportResource注解
// configurationClass属性值设置为lite
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
return true;
}
isFullConfigurationCandidate()方法
用来判断一个类是否加了@Configuration注解
// 含有@Configuration注解
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
isLiteConfigurationCandidate()方法
用来判断类是否加了@Bean,@Component,@ComponentScan,@Import,@ImportResource注解:
private static final Set<String> candidateIndicators = new HashSet<>(8);
// 类加载至JVM时,向集合中添加了四个元素
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
// 判断是否含有candidateIndicators这个集合中的注解
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
// candidateIndicators 是一个静态常量,在初始化时,包含了四个元素
// 分别为@Component,@ComponentScan,@Import,@ImportResource这四个注解
// 只要这个类上添加了这四种注解中的一个,就便是这个类是一个配置类,
// 这个类对应的BeanDefinition中的configurationClass属性值为lite
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// 查找有没有加了@Bean注解的方法
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
return false;
}
}
3.1.2 [3.4]parser.parse()
入参是配置类的集合,一次性处理全部的配置类!
该方法调用的是
ConfigurationClassParser.parse()
,ConfigurationClassParser类,根据类名就能猜测出,这个类是用来解析配置类的。
parse()方法会解析配置类上的注解:
- 提取@ComponentScan路径下的BeanDefinition,会递归调用
- @Import注解
- @ImportResource注解
- 加了@Bean注解的方法
解析出的结果会放入到parser对象的configurationClasses这个属性中(这个属性是个Map)。parse会将@Import注解要注册的类解析为BeanDefinition,但是不会把解析出来的BeanDefinition放入到BeanDefinitionMap中,真正放入到map中是在[3.7]这一行代码实现的:
this.reader.loadBeanDefinitions(configClasses)
下面先看下parse()的具体代码parser.parse(candidates), parse()方法需要一个参数,参数candidates是一个集合,表示配置类,可能是full或lite,集合中的元素个数由我们写的这一行代码决定:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
parse()具体代码:
该方法会调用另一个parse()方法,入参是配置类对应的BeanDefinition具体的子类,parse方法跟进去,会调用processConfigurationClass()
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
// 根据BeanDefinition类型的不同,调用parse()不同的重载方法
// 实际上最终都是调用processConfigurationClass()方法
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
//[1]标注@Configuration标签的类型会是AnnotatedBeanDefinition
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());
}
}
}
// 处理延迟importSelector
processDeferredImportSelectors();
}
[1]标注@Configuration标签的full配置类会是AnnotatedBeanDefinition类型:
3.1.2.1 ConfigurationClassParser.processConfigurationClass()
该方法的核心方法为doProcessConfigurationClass();
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 处理配置类,由于配置类可能存在父类(若父类的全类名是以java开头的,则除外),所有需要将configClass变成sourceClass去解析,然后返回sourceClass的父类。
// 如果此时父类为空,则不会进行while循环去解析,如果父类不为空,则会循环的去解析父类
// SourceClass的意义:简单的包装类,目的是为了以统一的方式去处理带有注解的类,不管这些类是如何加载的
// 如果无法理解,可以把它当做一个黑盒,不会影响看spring源码的主流程
SourceClass sourceClass = asSourceClass(configClass);
do {
// 核心处理逻辑
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
// 将解析的配置类存储起来,这样回到parse()方法时,能取到值
this.configurationClasses.put(configClass, configClass);
}
3.1.2.2 ConfigurationClassParser.doProcessConfigurationClass()
该方法的作用是解析入参中的配置类
- 提取@ComponentScan路径下的BeanDefinition,会递归调用
- @Import注解
- @ImportResource注解
- 加了@Bean注解的方法。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// [1]首先处理内部类,处理内部类时,最终还是调用doProcessConfigurationClass()方法
processMemberClasses(configClass, sourceClass);
// [2]处理属性资源文件,加了@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或者@ComponentScans注解
// [3.1] 先找出配置类上的@ComponentScan和@ComponentScans注解的所有属性(例如basePackages等属性值)
// ComponentScan注解除了最常用的basePackage之外,还有includeFilters,excludeFilters等
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) {
// 3.2 解析@ComponentScan和@ComponentScans配置的扫描的包所包含的类
// 比如 basePackages = com.tiantang.study, 那么在这一步会扫描出这个包及子包下的class,然后将其解析成BeanDefinition
// (BeanDefinition可以理解为等价于BeanDefinitionHolder)
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 3.3 通过上一步扫描包com.tiantang.com下的类,有可能扫描出来的bean中可能也添加了ComponentScan或者ComponentScans注解.
//所以这里需要循环遍历一次,进行递归(parse),继续解析,直到解析出的类上没有ComponentScan和ComponentScans
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 同样,这里会调用ConfigurationClassUtils.checkConfigurationClassCandidate()方法来判断类是否是一个配置类
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
//递归方法parse()
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 4.处理Import注解注册的bean,这一步只会将import注册的bean变为ConfigurationClass,不会变成BeanDefinition
// 而是在loadBeanDefinitions()方法中变成BeanDefinition,再放入到BeanDefinitionMap中
// 关于Import注解,后面会单独写文章介绍
//@Import注解是spring中很重要的一个注解,Springboot大量应用这个注解
//@Import三种类,一种是Import普通类,一种是Import ImportSelector,还有一种是Import ImportBeanDefinitionRegistrar
//getImports(sourceClass)是获得import的内容,返回的是一个set
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 5.处理@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注解的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// ... 省略部分代码
// No superclass -> processing is complete
return null;
}
-
递归处理内部类,一般不会使用内部类。
-
处理@PropertySource注解,@PropertySource注解用来加载properties文件。
-
从配置类中提取@ComponentScan注解的具体内容,ComponentScan注解除了最常用的basePackage之外,还有includeFilters,excludeFilters等。
[3.1] 先找出配置类上的@ComponentScan和@ComponentScans注解的所有属性
[3.2] 解析@ComponentScan
和@ComponentScans配置的扫描的包所包含的类,调用componentScanParser.parse()方法
[3.3] 通过上一步扫描包com.tiantang.com
下的类,有可能扫描出来的bean中可能也添加了ComponentScan或者ComponentScans注解,所以这里需要循环遍历一次,进行递归调用(parse)
,继续解析,直到解析出的类上没有ComponentScan和ComponentScans -
处理
Import注解
注册的bean,这一步只会将import注册的bean变为ConfigurationClass,不会变成BeanDefinition, 而是在loadBeanDefinitions()方法中变成BeanDefinition,再放入到BeanDefinitionMap中参见《3.1.3 [3.7]this.reader.loadBeanDefinitions()》章节
@Import注解是spring中很重要的一个注解,Springboot大量应用这个注解,@Import三种类,一种是Import普通类,一种是Import ImportSelector,还有一种是Import ImportBeanDefinitionRegistrar
-
处理@ImportResource注解引入的配置文件
-
处理@Bean的方法,可以看到获得了带有@Bean的方法后,不是马上转换成BeanDefinition,而是先用一个set接收。
3.1.2.2.1 ComponentScanAnnotationParser.parse()
该方法提供扫描指定路径下类的功能:
3.1.3 [3.7]this.reader.loadBeanDefinitions()
该方法实际上是将通过@Import、@Bean等注解方式注册的类解析成BeanDefinition,然后注册到BeanDefinitionMap中。
ConfigurationClassParser 类的[3.4] parse(candidates);虽然解析了类,并进行注册,但是@Import和@Bean这2个标签没有注册为BeanDefinition。而是放在[3.7]处进行处理
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
// 循环调用loadBeanDefinitionsForConfigurationClass()
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 省略部分代码 ...
// 如果一个bean是通过@Import(ImportSelector)的方式添加到容器中的,那么此时configClass.isImported()返回的是true
// 而且configClass的importedBy属性里面存储的是ConfigurationClass就是将bean导入的类
// 这一步的目的是
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 判断当前的bean中是否含有@Bean注解的方法,如果有,需要把这些方法产生的bean放入到BeanDefinitionMap当中
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 如果bean上存在@Import注解,且import的是一个实现了ImportBeanDefinitionRegistrar接口,则执行ImportBeanDefinitionRegistrar的registerBeanDefinitions()方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
3.2 postProcessBeanFactory()方法
该抽象方法定义在爷爷BeanFactoryPostProcessor接口中。
该方法是对BeanFactory进行处理,用来干预BeanFactory的创建过程。主要干了两件事:
-
(1)对加了
@Configuration
注解的类进行CGLIB代理
。对应下面源码中的[2]处代码 -
(2)向Spring中添加一个后置处理器ImportAwareBeanPostProcessor。对应下面源码中的[3]处代码
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
// [1]下面的if语句不会进入,因为在执行BeanFactoryPostProcessor时,会先执行BeanDefinitionRegistryPostProcessor的postProcessorBeanDefinitionRegistry()方法
// 而在执行postProcessorBeanDefinitionRegistry方法时,都会调用processConfigBeanDefinitions方法,这与postProcessorBeanFactory()方法的执行逻辑是一样的
// postProcessorBeanFactory()方法也会调用processConfigBeanDefinitions方法,为了避免重复执行,所以在执行方法之前会先生成一个id,将id放入到一个set当中,每次执行之前
// 先判断id是否存在,所以在此处,永远不会进入到if语句中
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
// 该方法在这里不会被执行到
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
//[2] 对加了@Configuration注解的配置类进行Cglib代理
enhanceConfigurationClasses(beanFactory);
// [3]添加一个BeanPostProcessor后置处理器
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
3.2.1 CGLIB增强Configuration类
更详细原理和作用请参见《@Configuration详解、CGLIB》
利用enhanceConfigurationClasses(beanFactory)方法对标记@Configuration的类进行增强,采用CGLIB来创建动态代理
利用enhanceConfigurationClasses(beanFactory)方法对Configuration类进行增强,采用CGLIB来创建动态代理:
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
// 省去部分代码...
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
// 省去部分代码...
// 调用ConfigurationClassEnhancer.enhance()方法创建增强类
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
// 省去部分代码...
}
}
ConfigurationClassEnhancer.enhance()方法:
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
// 省略部分代码。。。。
// 核心代码为 newEnHancer()
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
// 省略部分代码。。。。
return enhancedClass;
}
ConfigurationClassEnhancer.newEnhancer()方法:
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
// CGLIB的动态代理基于继承
enhancer.setSuperclass(configSuperClass);
// 为新创建的代理对象设置一个父接口
enhancer.setInterfaces(new Class<?>[] {
EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// 添加了两个MethodInterceptor。(BeanMethodInterceptor和BeanFactoryAwareMethodInterceptor)
// 通过这两个类的名称,可以猜出,前者是对加了@Bean注解的方法进行增强,后者是为代理对象的beanFactory属性进行增强
// 被代理的对象,如何对方法进行增强呢?就是通过MethodInterceptor拦截器实现的
// 类似于SpringMVC中的拦截器,每次执行请求时,都会对经过拦截器。
// 同样,加了MethodInterceptor,那么在每次代理对象的方法时,都会先经过MethodInterceptor中的方法
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
经过代理增强后,配置类会增加对EnhancedConfiguration接口的实现,间接实现实现BeanFactoryAware接口,会被注入beanFactory属性,而负责注入的就是内部类ImportAwareBeanPostProcessor
后置处理器
3.2.2 添加ImportAwareBeanPostProcessor后置处理器
会向spring容器中添加一个BeanPostProcessor后置处理器:ImportAwareBeanPostProcessor,BeanPostProcessor后置处理器最终会在Bean实例化和初始化的过程中执行,参与Bean的创建过程。
作用是2个,通过重写的2个方法来实现:
- postProcessPropertyValues ()
为被CGLIB增强时实现了EnhancedConfiguration
接口的代理类,设置beanFactory属性 - postProcessBeforeInitialization()
这个是对实现ImportAware接口的类进行通用处理
private static class ImportAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
private final BeanFactory beanFactory;
public ImportAwareBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
// 为被CGLIB增强时实现了EnhancedConfiguration接口的代理类,设置beanFactory属性
if (bean instanceof EnhancedConfiguration) {
((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
}
return pvs;
}
//这个是对实现ImportAware接口的类进行通用处理
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof ImportAware) {
ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
AnnotationMetadata importingClass = ir.getImportingClassFor(bean.getClass().getSuperclass().getName());
if (importingClass != null) {
((ImportAware) bean).setImportMetadata(importingClass);
}
}
return bean;
}
}
4. 总结
- 本文主要分析了 ConfigurationClassPostProcessor 类的作用,由于该类实现了 BeanFactoryPostProcessor 接口和 BeanDefinitionRegistryPostProcessor 接口,所以会重写 postProcessBeanDefinitionRegistry() 方法和 postProcessBeanFactory() 方法。
- 在postProcessBeanDefinitionRegistry()方法中解析了加了Configuration注解的类,同时解析出 @ComponentScan 和 @ComponentScans 扫描出的Bean,也会解析出加了 @Bean 注解的方法所注册的Bean,以及通过 @Import 注解注册的Bean和 @ImportResource 注解导入的配置文件中配置的Bean。在 postProcessBeanDefinitionRegistry() 方法中,通过源码分析了两个十分重要的方法:ConfigurationClassParser.parse()和this.reader.loadBeanDefinitions()
- 在postProcessBeanFactory()方法中,会利用CGLIB对加了@Configuration注解的类创建动态代理,进行增强。最后还会向spring容器中添加一个Bean后置处理器:ImportAwareBeanPostProcessor
5. 疑问
1) 一个配置类必须加@Configuration注解?不加就不能被Spring解析了吗?例如如下代码:
//配置类,仅有@ComponentScan,没有@Configuration
@ComponentScan("com.tiantang.study")
public class AppConfig {
}
public class MainApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
(2) 为什么加了@Configuration注解的类需要被CGLIB进行增强?
参考:《ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!》