Spring中,Bean的生命周期历经以下几个阶段:
- BeanDefinition阶段: 即Bean定义阶段。在bean被创建之前,bean是以
BeanDefinition的方式被描述和注册在BeanFactory中的。此阶段通过解析配置获得BeanDefinition,然后将其存储到容器中用于后续处理 - Bean实例化阶段: 借助解析配置类后得到的
BeanDefinition(即Bean定义),对Bean进行实例化,创建出bean对象 - Bean属性注入阶段: 此阶段前,会先收集Bean的初始化、销毁及依赖注入相关的元数据信息,然后根据元数据对Bean中需注入的属性或方法执行依赖注入
- Bean初始化阶段: 对Bean进行初始化,按照顺序执行初始化方法,顺序为先执行
@PostConstruct注解标记的方法、然后是实现InitializingBean接口重写的afterPropertiesSet方法、最后是init-method方法 - Bean销毁阶段:销毁当前Bean,按顺序执行其销毁方法,顺序为执行
@PreDestroy注解标记的方法、然后是实现DisposableBean接口重写的destroy方法、最后是destroy-method方法
其他阶段的解析参考:
简介
本文介绍Bean定义阶段,主要流程是:通过解析配置类来获得Bean定义,并将Bean定义注册到容器中,后续根据Bean定义来对Bean进行实例化、属性注入、初始化等操作。
由于xml的配置已不常用,下面对注解配置类的解析过程进行简单概括:
注解配置类的解析发生在容器刷新时( 主启动类run方法中 refreshContext方法)的BeanDefinitionRegistryPostProcessor 执行阶段。BeanDefinitionRegistryPostProcessor是一个后置处理器,它执行时会获取一个自己的实现类 ConfigurationClassPostProcessor ,该实现用于解析配置类,其主要负责的工作分为两步:
- 解析配置类:创建一个
ConfigurationClassParser来解析配置类,这里的配置类指的不光是@Configuration类,还包括@Component、@Import、@PropertySource等 - 注册Bean定义:将配置类的解析结果构建为
BeanDefinition,并注册到容器中 ,后续Spring会通过BeanDefinition来对Bean进行实例化、属性注入、初始化等操作
第一步,解析注解配置类的具体过程
源码中处理的是如下几个注解:
-
处理
@Component注解。这里是处理标记了@Component(也是@Configuration)的配置类中的内部类,保证其内部类包含某些配置时也能被正确处理。当配置类中有内部类时,会递归进行解析处理,没有则跳过-
这里的递归解析就是递归执行配置类解析器
ConfigurationClassParser的配置解析方法,用该方法去解析配置类的内部类,同时该方法也是负责解析所有的注解配置类的方法,所以是递归解析。 -
因为
@Configuration其实就相当于是@Component, 所以这里也会处理标记了@Configuration的配置类中的内部类。可通过@Configuration源码得知原因:@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component // @Configuration注解上标有@Component public @interface Configuration { @AliasFor(annotation = Component.class) String value() default ""; boolean proxyBeanMethods() default true; }
-
-
处理
@PropertySource注解。主要是根据此注解中的路径,来加载对应的资源文件,将其添加进Environment中(Environment包含Profile和Property:Profile用于配置激活的环境,Property则用于从外部配置如配置文件、系统环境变量、命令行等读取属性并注入到bean组件中) -
判断当前类是否有标记了
@ComponentScans或@ComponentScan注解。如果有则循环处理,根据注解中的路径使用扫描器ClassPathBeanDefinitionScanner进行扫描,将扫描到符合条件的类(默认被@Component注解标注的类)包装为BeanDefinition并注册到容器中。如果扫描到的类中有配置类,还会通过BeanDefinition对扫描到的配置类进行递归解析这里的递归就是依然再执行`ConfigurationClassParser`配置类解析器的解析方法,解析扫描到的配置类中的`@Component`、 `@Bean`、`@Import` 、`@PropertySource` 、`@ImportResource`等注解 -
处理
@Import注解,将此注解导入的类解析为BeanDefinition并注册到容器中。它能导入的值有4种类型:ImportSelector类型:此类型的selectImports方法会返回类名,通过类名将相应的类解析为bean,通常使用中都是返回配置类和普通类的类名。如果是配置类则还是ConfigurationClassParser递归解析;普通类会转为配置类进行处理,并在后续注册bean定义时注册到容器中DeferredImportSelector类型:也是通过selectImports方法获得配置类或普通类的类名,然后通过类名将对应的类解析为bean。但此类型是延迟进行解析处理,时机在注解配置类都解析完成之后ImportBeanDefinitionRegistrar类型,此类型的registerBeanDefinitions方法用于创建BeanDefinition,并将其存入bean定义注册列表中,后续在注册Bean定义步骤时处理- 导入普通类和配置类:配置类依然是
ConfigurationClassParser递归解析;普通类会被封装为一个配置类,存入ConfigurationClassParser类的集合类型成员变量configurationClasses中,后续在注册bean定义时注册到容器中
-
处理
@ImportResource注解,取出注解中的配置文件路径缓存起来,后续在注册Bean定义步骤时进行处理 -
处理
@Bean注解,获取所有标记@Bean注解的方法,存起来后续在注册Bean定义步骤时处理,跟上面的@ImportResource一样。 -
处理配置类的父类。如果配置类存在父类的话,父类也应该一起加载,这里会递归调用注解配置类的解析方法,再次进入上面几个注解的处理,直到所有配置类都处理完毕。
第二步,注册
BeanDefinition时的具体处理过程:
注册BeanDefinition时主要处理三项内容:
@Import导入的ImportBeanDefinitionRegistrar和普通类@Bean注解标记的方法@ImportResource导入的外部配置文件
为什么只是上面三项处理? 因为除了@ComponentScans 与@ComponentScan有点特殊之外(直接将扫描到的类转为BeanDefinition 注册到容器中),只有上面三项是产生了解析结果的:
- 当配置类标记的是
@Component注解时,只是递归处理其内部类,相当于重新进入了@Component、@PropertySource、@ComponentScans与@ComponentScan、@Import、@ImportResource、@Bean这些注解的处理流程,并不是标记了@Component就直接解析为BeanDefinition @PropertySource注解是将配置文件作为属性源添加到环境中,它主要是修改BeanDefinition而不是创建。应用此注解后底层会通过PropertySourcesPlaceholderConfigurer解析BeanDefinition中的占位符,并使用属性源中的属性替换BeanDefinition中对应的属性@ComponentScans与@ComponentScan将扫描到的类转为BeanDefinition存入容器中之后,还会通过这些BeanDefinition重新进入@Component、@PropertySource、@ComponentScans与@ComponentScan、@Import、@ImportResource、@Bean这些注解的处理循环,因为扫描到的类可能也是配置类- 如果
@Import导入的是非ImportBeanDefinitionRegistrar类型,其实还是递归进行@Component、@PropertySource、@ComponentScans与@ComponentScan、@Import、@ImportResource、@Bean这些注解的解析(没有任何注解的普通java类是一种特殊情况,它也可以被@Import导入,但不会被递归解析处理,源码中是将其转为配置类,然后在注册bean定义时根据类信息创建出BeanDefinition对象进行注册)
处理过程:
- 针对
@Import导入的ImportBeanDefinitionRegistrar,会从其registerBeanDefinitions方法中直接取出bean定义进行注册;对于@Import导入的配置类,将配置类本身作为bean注册进容器中;对于@Import导入的普通类,根据类信息创建一个bean定义注册进容器中 - 针对
@Bean注解标记的方法,主要是根据@Bean注解中的各项属性和方法信息来封装对应的BeanDefinition并注册到容器中 - 针对
@ImportResource导入的外部配置文件,解析从@ImportResource上取到的配置文件的路径,读取其内容解析为BeanDefinition并注册到容器中。日常使用较多是导入xml文件,对于xml文件,会使用XmlBeanDefinitionReader读取其内容构建为BeanDefinition注册到容器中 - bean定义注册到容器中,保存的位置在容器默认实现
DefaultListableBeanFactory类的beanDefinitionMap集合中
下面根据源码进行具体的分析。
源码入口
源码版本:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
</parent>
主启动类
run方法中prepareContext进行容器初始化

prepareContext 方法中的如下位置或获取并加载主启动类,getAllSources会获取主启动类,load方法为把主启动类注册为BeanDefinition并存入容器中:

主启动类
run方法中refreshContext进行刷新

刷新方法触发到
AbstractApplicationContext的refresh()方法,执行其中的invokeBeanFactoryPostProcessors(),该方法会执行BeanFactoryPostProcessor后置处理器:

注意:
BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor,所以也会执行BeanDefinitionRegistryPostProcessor- 但是源码中,是先执行
BeanDefinitionRegistryPostProcessor,然后再执行BeanFactoryPostProcessor的
进入上图的
invokeBeanFactoryPostProcessors()后,发现是通过代理去执行的:

再进入代理执行的
invokeBeanFactoryPostProcessors
方法比较长,概括其最核心的一点就是执行实现了 PriorityOrdered 接口的后置处理器BeanDefinitionRegistryPostProcessor ,该处理器的 postProcessBeanDefinitionRegistry 方法会创建一个配置类解析器 ConfigurationClassParser 来解析配置类:
//beanFactory: 可配置的列表式Bean工厂(通常是DefaultListableBeanFactory),包含了所有尚未实例化的Bean定义。
//beanFactoryPostProcessors: 一个列表,包含通过编程方式手动添加的 BeanFactoryPostProcessor
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
// 先执行后置处理器--BeanDefinitionRegistryPostProcessor
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 首先处理参数列表传入的后置处理器(第二个参数),执行其注册方法,向容器中添加bean定义
// 参数中的后置处理器是由`run`方法中的`prepareContext`方法添加而来的,位置就在`refreshContext`方法的上一行
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
// 如果参数中的后置处理器是BeanDefinitionRegistryPostProcessor类型
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
// 转为该类型
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
// 执行其postProcessBeanDefinitionRegistry方法,用于注册bean定义
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
// 如果参数列表中的处理器不是BeanDefinitionRegistryPostProcessor及其实现,存入普通处理器集合
// 实际这里存入的是BeanFactoryPostProcessor,目的是把BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor分隔起来处理
regularPostProcessors.add(postProcessor);
}
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
/******* 下面执行的是实现了`PriorityOrdered`的`BeanDefinitionRegistryPostProcessor`的bean定义注册方法 *****/
// 从容器中获取所有实现了BeanDefinitionRegistryPostProcessor的bean的bean名称
// 最后一个参数false表示不预先初始化单例对象,只查找定义。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 循环上面找到的所有bean名
for (String ppName : postProcessorNames) {
// 根据bean名去判断该bean是否又实现了PriorityOrdered
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 将实现了PriorityOrdered的BeanDefinitionRegistryPostProcessor存入集合
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
// 将实现了PriorityOrdered的bean名称存入processedBeans集合中
processedBeans.add(ppName);
}
}
// 对实现了PriorityOrdered的BeanDefinitionRegistryPostProcessor进行排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 全部存入registryProcessors集合中
registryProcessors.addAll(currentRegistryProcessors);
// 执行所有实现了PriorityOrdered的BeanDefinitionRegistryPostProcessor的bean定义注册方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
// 清空集合,用于下一个处理
currentRegistryProcessors.clear();
/*********** 执行实现了`Ordered`的`BeanDefinitionRegistryPostProcessor`的bean定义注册方法 ***********/
// 依旧是获取所有实现了BeanDefinitionRegistryPostProcessor的bean的bean名称
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 循环上面找到的所有bean名
for (String ppName : postProcessorNames) {
// 根据bean名去判断该bean是否又实现了Ordered,并且不包含在上面PriorityOrdered实现的bean名中
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
// 添加到集合
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 此时registryProcessors中包含实现了PriorityOrdered、Ordered的BeanDefinitionRegistryPostProcessor
registryProcessors.addAll(currentRegistryProcessors);
// 执行所有实现了Ordered的BeanDefinitionRegistryPostProcessor的bean定义注册方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
// 清空集合,用于下一个处理
currentRegistryProcessors.clear();
/*********** 执行除上面两种之外,剩余的BeanDefinitionRegistryPostProcessor的bean定义注册方法 ********/
boolean reiterate = true;
while (reiterate) {
reiterate = false;
// 依旧是获取所有实现了BeanDefinitionRegistryPostProcessor的bean的bean名称
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
// 添加到集合
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
// 排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 此时registryProcessors中包含所有的BeanDefinitionRegistryPostProcessor
registryProcessors.addAll(currentRegistryProcessors);
// 执行bean定义注册方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
// 执行所有BeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法
// postProcessBeanFactory方法由父类BeanFactoryPostProcessor继承而来
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
// 执行没实现BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor的postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// 在BeanFactory没有实现BeanDefinitionRegistry的情况下,
// 执行BeanFactoryPostProcessor的postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 上面处理完了BeanDefinitionRegistryPostProcessor,这里开始处理BeanFactoryPostProcessor
// 获取所有实现了BeanFactoryPostProcessor的bean的bean名称
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
// 循环bean名
for (String ppName : postProcessorNames) {
// 跳过上面处理BeanDefinitionRegistryPostProcessor时已经处理的
// 因为所有BeanDefinitionRegistryPostProcessor也是BeanFactoryPostProcessor,不重复执行
if (processedBeans.contains(ppName)) {
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 将实现了PriorityOrdered的BeanFactoryPostProcessor存入集合
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
// 将实现了Ordered的BeanFactoryPostProcessor存入集合
orderedPostProcessorNames.add(ppName);
}
else {
// 将其剩余的BeanFactoryPostProcessor存入集合
nonOrderedPostProcessorNames.add(ppName);
}
}
// 排序,并执行实现了PriorityOrdered的BeanFactoryPostProcessor的postProcessBeanFactory方法
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// 排序,执行实现了Ordered的BeanFactoryPostProcessor的postProcessBeanFactory方法
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// 最后, 执行所有剩余的其他BeanFactoryPostProcessors的postProcessBeanFactory方法
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// 清除合并bean定义缓存,确保后续流程(Bean实例化、初始化等)使用的是最新的、已被处理过的 Bean 定义
beanFactory.clearMetadataCache();
}
简单概括
invokeBeanFactoryPostProcessors方法的执行机制如下,顺序是:
- 先执行
BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法- 然后再执行
BeanFactoryPostProcessor的postProcessBeanFactory方法
下面是具体执行过程,这里只需要知道配置类的解析过程发生在实现了PriorityOrdered 接口的BeanDefinitionRegistryPostProcessor 中即可:
- 先执行
invokeBeanFactoryPostProcessors方法第二个参数中所包含的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法(方法参数中的后置处理器是由run方法中的prepareContext方法添加来的,位置就在refreshContext方法的上一行) - 然后执行容器中实现了下列接口的
BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法- 先执行实现了
PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法 - 然后执行实现了
Ordered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法 - 最后执行除上面两种之外其他所有的
BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
- 先执行实现了
- 最后执行容器中实现了下列接口的
BeanFactoryPostProcessor的postProcessBeanFactory方法- 先执行实现了
PriorityOrdered接口的BeanFactoryPostProcessor的postProcessBeanFactory方法 - 然后执行实现了
Ordered接口的BeanFactoryPostProcessor的postProcessBeanFactory方法 - 最后执行除上面两种之外其他所有的
BeanFactoryPostProcessor的postProcessBeanFactory方法
- 先执行实现了
注解配置类解析处理器
下面看注解配置类是如何被解析的,在invokeBeanFactoryPostProcessors方法中,它会先执行所有实现了 PriorityOrdered 接口的 BeanDefinitionRegistryPostProcessor处理器,而这些处理器实现中的ConfigurationClassPostProcessor,就是用来解析注解配置类的。
为什么源码中是先执行实现了 PriorityOrdered 接口的:
PriorityOrdered是 Spring 框架中一个重要的排序接口,拥有最高优先级。它的核心价值在于让关键基础组件优先初始化,比如属性占位符配置器必须在其他Bean之前处理,否则依赖注入时会找不到解析占位符的组件
Spring 的排序系统分为三个层次:
- PriorityOrdered 对象:最高优先级
- Ordered 对象:中等优先级
- 普通对象(未实现任何排序接口):最低优先级
方法中会先执行的处理器(也是
BeanDefinitionRegistryPostProcessor的实现类)是ConfigurationClassPostProcessor,它主要用来解析注解配置类。触发位置如下图
在invokeBeanFactoryPostProcessors方法内的invokeBeanDefinitionRegistryPostProcessors:

进入invokeBeanDefinitionRegistryPostProcessors,到达如下位置后,再进入postProcessBeanDefinitionRegistry内:

postProcessBeanDefinitionRegistry会调用到ConfigurationClassPostProcessor类, 其方法体中的如下位置即为解析配置类操作,作用就是解析配置类中定义的 bean ,并封装为BeanDefinition。

上图的ConfigurationClassPostProcessor.processConfigBeanDefinitions方法有两步重要操作:
- 筛选出配置类
- 解析筛选出的配置类
下面分别介绍
筛选配置类
进入上图的
processConfigBeanDefinitions方法内,先看配置类筛选逻辑:
这里筛选出的配置类,就是标记有@Configuration、@Component、@ComponentScan、@Import、@ImportResource注解的类:
-
第一步,先从容器中获取bean定义,因为
run方法运行时调用的prepareContext方法会把主启动类注册到容器中,所以第一处红框的candidateNames中包含主启动类 -
第二步,
checkConfigurationClassCandidate方法会检查注解,主启动类@SpringBootApplication注解中包含了@SpringBootConfiguration,后者其实就是一个@Configuration,所以主启动类的bean定义被放入了configCandidates集合中,后续的配置类解析的来源就是这个集合。

进入ConfigurationClassUtils.checkConfigurationClassCandidate方法,看具体的筛选逻辑源码 ,核心的逻辑是下半部分,判断类上是否标注@Configuration、@Component、@ComponentScan、@Import、@ImportResource注解:
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
// 通过bean定义获取类名
String className = beanDef.getBeanClassName();
// 如果Bean是通过工厂方法定义的(如@Bean方法)、或者是没有类名的(如动态代理生成的Bean),都不满足筛选条件
if (className == null || beanDef.getFactoryMethodName() != null) {
// 返回false代表没有被选上
return false;
}
AnnotationMetadata metadata;
// 如果BeanDefinition是AnnotatedBeanDefinition,并且其内部已经解析过元数据,则直接使用
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// 可重用给定BeanDefinition中预解析的元数据
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
// 如果BeanDefinition是AbstractBeanDefinition类型,并且已经加载了类
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// 检查该类是否为下面这些特定接口的实现。如果是则返回false;否则通过反射获取注解元数据
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);
}
// bean定义既不是AnnotatedBeanDefinition类型也不是AbstractBeanDefinition类型
// 则使用MetadataReaderFactory读取类元数据(通过ASM避免直接加载类)。
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;
}
}
// 尝试获取@Configuration注解
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 如果类上标注了@Configuration注解,并且proxyBeanMethods不为false(默认为true)
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
// 则标记为Full配置类,即配置内容都是单例的
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 如果类上标注了@Configuration,且proxyBeanMethods为false
// 或者类上有`@Component`、`@ComponentScan`、`@Import`、`@ImportResource`注解
else if (config != null || isConfigurationCandidate(metadata)) {
// 则标记为Lite配置类,该模式配置类中的配置内容不是单例的,每次获取都是创建新的实例
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
// 上面两个条件都不满足,则不认为其是一个配置类,不会被筛选到
else {
return false;
}
// 处理排序
// 如果配置类上有@Order注解或实现了Ordered接口,则获取其order值并设置到BeanDefinition的属性中。
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
// 返回true,满足筛选条件。表示该BeanDefinition是一个配置类
return true;
}
解析配置类
下面是
ConfigurationClassPostProcessor.processConfigBeanDefinitions方法最关键的部分,就是初始化了一个ConfigurationClassParser,它是用来解析注解配置类的核心,在其parse方法中会解析上面收集到的配置类
项目启动后,最先获取到的配置类只有主启动类。在parse方法中,会通过主启动类注解@SpringBootApplication中的子注解@ComponentScan扫描到项目中的其他配置类,然后对扫描到的所有配置类进行递归解析

进入
parse方法:
方法体内会根据bean定义的不同类型走不同的处理流程。且当for循环调用完成后,最底下会让 deferredImportSelectorHandler 执行 process 方法,让@Import导入的DeferredImportSelector类型的配置延迟处理(这就是DeferredImportSelector会在注解配置类解析完成后才执行的原因,下面会有单独的章节介绍此机制):
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 如果bean定义是AnnotatedBeanDefinition类型,则使用其元数据(metadata)进行解析
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
// 如果是AbstractBeanDefinition类型并且已经加载了Bean类,则直接使用该类进行解析
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
// 都不是,使用类名字符串进行解析(可能需要加载类)
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
} // catch ......
}
// 回调特殊的ImportSelector,延迟解析某些配置类
this.deferredImportSelectorHandler.process();
}
下面介绍配置类的详细解析过程
配置类解析过程
继续看上面
parse方法:
进入其中,有3种分支parse处理,这里把它们都展示出来:
protected final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
可以看到上面三个parse 方法中都用了同一个processConfigurationClass方法解析注解配置类,进入其中,找到doProcessConfigurationClass,它负责实际解析配置类:

doProcessConfigurationClass方法内部会处理多种注解配置,下面进入其中
// configClass 当前配置类
// sourceClass 当前配置类的源class
// filter 需要排除不进行解析处理的类,其值是类路径字符串,默认值在ConfigurationClassParser的DEFAULT_EXCLUSION_FILTER中
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 如果当前配置类被@Component、@Configuration注解标记
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 递归调用doProcessConfigurationClass方法来解析当前配置类中的内部类
// 确保内部配置类优先处理,因为它们可能包含影响外部类处理的配置
processMemberClasses(configClass, sourceClass, filter);
}
// 处理 @PropertySource 和 @PropertySources 注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
// 环境必须是可配置环境才可以处理 @PropertySource 和 @PropertySources
if (this.environment instanceof ConfigurableEnvironment) {
// 此处会解析属性文件位置(支持占位符),创建 PropertySource 对象并将其添加到环境中。
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 处理 @ComponentScan 和 @ComponentScans 注解,执行组件扫描,并注册发现的 Bean
// 获取当前配置类中的 @ComponentScan 和 @ComponentScans
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
// @ComponentScan 和 @ComponentScans不为空,且满足设置的@Conditional条件(即不跳过),则进行处理
// shouldSkip方法,判断是否跳过某个组件的处理,false-不跳过;true-跳过。它的逻辑是:
// 1.先检查组件是否有@Conditional注解,没有则返回false
// 2.实例化所有Conditional条件,任何一个条件不匹配就返回true
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 根据注解中的basePackages、Filter等信息执行实际的扫描工作,将扫描到的Bean定义注册到容器中并返回
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 对扫描到的每个Bean定义,检查它是否也是配置类,如果是,则递归调用 parse() 方法处理
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 递归调用,此处还是会递归调用当前的doProcessConfigurationClass方法
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 处理 @Import 注解
// getImports会提取配置类上的所有@Import注解的value属性
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 处理 @ImportResource 注解,主要用于处理传统的XML配置文件
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
// 获取@ImportResource注解的locations值,即xml配置文件路径
String[] resources = importResource.getStringArray("locations");
// 获取@ImportResource注解的读取器类,它用来读取解析指定的配置文件。未指定时有默认读取器(根据资源后缀)
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
// 解析资源路径中的占位符,如:classpath:{my.path}/config.xml,将my.path解析为具体路径
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
// 将解析后的资源路径和reader添加到配置类中,后续进行加载
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 提取所有被 @Bean 注解标记的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
// 将方法包装为BeanMethod并添加到配置类中,后续进行处理。
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 处理接口默认方法上的 @Bean 注解,即java8中接口的default方法
// 此方法会遍历配置类实现的所有接口,检查接口默认方法上的 @Bean 注解。
processInterfaces(configClass, sourceClass);
// 处理配置类的父类,确保继承中的配置也被处理
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
// 跳过父类为空的、以及Java标准库中的类(java.开头的包)不处理
// knownSuperclasses用于避免死循环解析,首次遇到父类解析后存入缓存,再次遇到时跳过避免死循环解析
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
// 将父类全限定名与当前配置类关联存入缓存。
this.knownSuperclasses.put(superclass, configClass);
// 继续向上递归处理,当前类有父类,父类向上还有更多父类时依次向上处理,将所有注解配置进行合并
return sourceClass.getSuperClass();
}
}
// 没有父类需要处理时,返回 null,表示当前配置类已处理完毕
return null;
}
下面根据源码逐个分析每个注解的处理
处理@Component注解
doProcessConfigurationClass方法内第一步,会判断类中是否有标注@Component注解。因为所有的@Configuration类必定是@Component(参考@Configuration注解源码) ,所以此处的判断必进:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// 判断是否有标注 `@Component` 注解
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
//此方法的逻辑就是`递归解析内部的配置类`
processMemberClasses(configClass, sourceClass);
}
// .............
此方法中的processMemberClasses方法会获取标记@Component注解的类的内部类,如果有内部类,则递归调用 doProcessConfigurationClass方法进行解析处理,没有内部类则方法不执行(可以理解为此处只用来处理@Configuration配置类的内部类)
下面是
processMemberClasses的源码解析:
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) {
// 检查内部类是否有@Component、@ComponentScan、@Import、@ImportResource注解
// 确保内部类的类名不等于外部配置类的类名
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) && !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
// 满足上面两个条件才把内部类存入集合中,然后在下面进行处理
candidates.add(memberClass);
}
}
// 根据内部类实现的Ordered接口、或标记的@Order注解进行排序,确保配置类按既定的顺序处理
OrderComparator.sort(candidates);
// 循环处理每一个内部类
for (SourceClass candidate : candidates) {
// 避免循环导入。importStack用于跟踪当前正在处理的配置类
// 如果检测到当前外部配置类已经在导入栈中,说明出现了循环导入配置(如 A 导入 B,B 又导入 A)
if (this.importStack.contains(configClass)) {
// 当检测到循环导入时,通过problemReporter报告错误,但不会立即抛出异常,目的是可以继续处理其他配置
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
// 没有循环导入的情况
else {
// 将当前内部类所在的外部类存入importStack
this.importStack.push(configClass);
try {
// 递归调用doProcessConfigurationClass方法处理内部配置类
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
// 最后无论处理成功还是失败,将当前配置类从importStack导出
finally {
this.importStack.pop();
}
}
}
}
}
处理@PropertySource注解
接下来是
doProcessConfigurationClass方法第二步,处理@PropertySource注解。针对它的处理,会将注解中的资源文件添加进Environment中
AnnotationConfigUtils 能取出配置类上标注的所有注解信息,并进一步取出指定的注解属性。这里获取的就是 @PropertySource :
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// .......
// 处理 @PropertySource 和 @PropertySources 注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
// 环境必须是可配置环境才可以处理 @PropertySource 和 @PropertySources
if (this.environment instanceof ConfigurableEnvironment) {
// 此处会解析属性文件位置(支持占位符),创建 PropertySource 对象并将其添加到环境中。
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// ........
上面的
processPropertySource就是在真正的封装PropertySource导入的资源文件,进入其中:
主要的操作就是获取 @PropertySource 注解中的路径,根据路径加载资源文件,并添加进Environment中:
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
// 获取注解中的name属性
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
// 获取注解中的encoding属性
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
// 获取注解中的文件路径
String[] locations = propertySource.getStringArray("value");
// 断言判断,注解中需要至少指定了一个路径,否则报错
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
// 是否忽略资源未找到的错误。如果为 true,当文件不存在时只记录警告而不中断启动。
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
// 获取注解中指定的 factory 属性,默认为PropertySourceFactory
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
// 如果factory属性使用的是默认值,则指定为DefaultPropertySourceFactory,否则实例化一个factory属性中指定的工厂
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
// 循环处理每一个路径
for (String location : locations) {
try {
// 解析路径字符串中的占位符,将占位符解析为实际值。如 ${...}
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
// 将解析后的位置字符串转换为 Resource 对象
Resource resource = this.resourceLoader.getResource(resolvedLocation);
// 这里将资源文件加载到环境中
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
进入addPropertySource方法,方法内是通过从environment中获取MutablePropertySources,再通过MutablePropertySources将PropertySource添加到环境environment中的,如果存在相同名称的PropertySource,则进行合并操作,不存在相同名称的则直接存入environment:
private void addPropertySource(PropertySource<?> propertySource) {
// 获取属性源PropertySource的name属性
String name = propertySource.getName();
// 从环境environment中获取所有PropertySource
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
// 如果当前环境中已经包含了与参数同名的PropertySource,则进行合并
if (this.propertySourceNames.contains(name)) {
// 获取环境中已存在的同名PropertySource
PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
// 根据当前参数中的PropertySource创建一个新的PropertySource
// 如果当前参数中的PropertySource是ResourcePropertySource类型,则将其转为ResourcePropertySource
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
// 如果环境中存在与当前PropertySource同名的PropertySource,且其类型是CompositePropertySource复合类型
// 则将上面新建的PropertySource存入其最前端
if (existing instanceof CompositePropertySource) {
((CompositePropertySource) existing).addFirstPropertySource(newSource);
}
// 如果环境中与当前PropertySource同名的PropertySource,不是CompositePropertySource复合类型
else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
// 根据当前方法参数传入的propertySource名称创建一个新的复合属性源
CompositePropertySource composite = new CompositePropertySource(name);
// 将新建的propertySource与环境中已存在的同名propertySource都存入复合属性源中
// composite是用LinkedHashSet来存储propertySource,它是有序去重的集合,所以新的在前旧的在后。
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
// 将环境中当前已存在的propertySource替换为复合propertySource
// 这里就是把当前propertySource存入环境environment中了
propertySources.replace(name, composite);
}
// 合并完成后直接返回
return;
}
}
// 如果当前环境中没有与参数同名的PropertySource,下面分为了两种情况处理:
// 1.属性源名称集合为空
if (this.propertySourceNames.isEmpty()) {
// 将传入的propertySource添加到属性源列表的末尾,保证用户自定义属性源低于系统默认源
propertySources.addLast(propertySource);
}
// 2.属性源名称集合不为空,但没有当前参数中的PropertySource时
else {
// 将新的属性源添加到最近添加的那个属性源的前面。
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
propertySources.addBefore(firstProcessed, propertySource);
}
// 最后将当前方法参数的PropertySource的名称添加到propertySourceNames中,标记为已添加到环境中
this.propertySourceNames.add(name);
}
这里概括下合并操作,当环境中存在与当前PropertySource同名的PropertySource时,会将当前PropertySource转化为一个新的属性源:
- 如果该同名
PropertySource的类型是CompositePropertySource复合类型,则会将新属性源添加到复合属性源的最前端,复合属性源会通过LinkedHashSet来进行存储 - 如果该同名
PropertySource的类型不是CompositePropertySource复合类型,则根据当前属性源PropertySource的名称创建一个新的复合属性源,将已存在同名属性源和新属性源都存入这个新建的复合属性源中,最后把这个新建的复合属性源存入到environment中
处理@ComponentScan注解
接下来是
doProcessConfigurationClass方法对@ComponentScans与@ComponentScan注解的处理。
此处会判断配置类上是否有标记了@ComponentScans 与@ComponentScan注解,如果有则循环处理( @ComponentScans 注解可以组合多个 @ComponentScan 注解,所以这里是 for 循环解析 @ComponentScan ),逻辑如下:
- 根据注解中的路径进行扫描,将扫描到的、符合条件的类(默认被
@Component注解标注的类)包装为BeanDefinition并注册进容器中(bean定义保存在容器默认实现DefaultListableBeanFactory的beanDefinitionMap集合中) - 完成注册后,返回扫描到的
BeanDefinition,并判断BeanDefinition是不是配置类,如果是,则再根据BeanDefinition进行配置类的解析
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//...........
// 处理 @ComponentScan 和 @ComponentScans 注解,执行组件扫描并注册发现的 Bean
// 获取当前配置类中的 @ComponentScan 和 @ComponentScans
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
// @ComponentScan 和 @ComponentScans不为空,且满足设置的@Conditional条件(即不跳过),则进行处理
// shouldSkip方法,判断是否跳过某个组件的处理,false-不跳过;true-跳过。它的逻辑是:
// 1.先检查组件是否有@Conditional注解,没有则返回false
// 2.实例化所有条件,任何一个条件不匹配就返回true
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 根据注解中的basePackages、Filter等信息执行实际的扫描工作,将扫描到的Bean定义注册到容器中并返回
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 对扫描到的每个Bean定义,检查它是否也是配置类,如果是,则递归调用 parse() 方法处理
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 递归调用,此处还是会调用到当前的doProcessConfigurationClass方法
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//...................
}
@ComponentScan可以标注多个,并且 Spring 4.3 后多了一个@ComponentScans注解,它可以组合多个@ComponentScan注解,所以这里是 for 循环解析@ComponentScan- 扫描并返回
BeanDefinition的处理方法为this.componentScanParser.parse - 最后一行的
parse方法会对扫描到的BeanDefinition进行解析处理,内部还是递归调用ConfigurationClassParser的processConfigurationClass方法
下面先看扫描并返回
BeanDefinition的处理:this.componentScanParser.parse
此 parse 方法用来处理包扫描的工作,核心逻辑就是创建一个扫描器scanner,然后获取 @ComponentScan 注解中的各项属性赋值给这个扫描器(比如根据basePackages整理出要进行包扫描的包路径,其他还有nameGenerator、includeFilters、excludeFilters等),然后用扫描器进行扫描,将扫描到的 BeanDefinition 注册到容器中并返回。
进入这个 parse 方法,这里只看关键的最后一行:

该扫描器是
ClassPathBeanDefinitionScanner,进入其doScan方法内部,处理逻辑是:
- 只要扫描到了符合的类(默认被
@Component注解标注的类),就会将其包装为BeanDefinition,然后对这些BeanDefinition进行一些额外的处理(如作用域、bean名生成等),最后把BeanDefinition注册进容器中并返回 BeanDefinition在容器中的保存位置在容器默认实现DefaultListableBeanFactory的beanDefinitionMap集合中。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 确保包路径数组不为空
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 在指定包及其子包中找到类元数据,并转为Bean定义返回
// findCandidateComponents内:
// 1.通过ASM读取类元数据,避免加载类造成性能影响
// 2.获取类的条件是非接口、非抽象类、含特定注解(如@Component)
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 确定Bean的作用域。resolveScopeMetadata方法会检查类上的 @Scope 注解
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
// 将作用域设置到当前Bean定义中
candidate.setScope(scopeMetadata.getScopeName());
// 为bean生成一个唯一的bean名称,生成的规则是:
// 1.如果类上有 @Component("customName"),则使用指定的名称
// 2.否则,基于类名生成默认名称(如 myServiceImpl)
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
// 按类型设置 Bean 的默认自动装配模式
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 处理类上的通用注解(如 @Lazy、@Primary、@DependsOn、@Role、@Description)
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查是否已存在同名的Bean定义。如果不存在同名Bean,返回true
if (checkCandidate(beanName, candidate)) {
// BeanDefinitionHolder用来包装Bean定义及其名称,方便传递和管理
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 根据 @Scope 注解的 proxyMode 属性获取代理模式并应用
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 将bean定义添加到集合
beanDefinitions.add(definitionHolder);
// 将bean定义注册到容器中
// 这里是直接将bean定义存入容器中,默认是在DefaultListableBeanFactory类的beanDefinitionMap中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
// 返回含有bean定义的集合
return beanDefinitions;
}
然后是
parse方法,根据返回的BeanDefinition进行配置类的解析
完成扫描并获取到Bean定义后,还会再调用一个parse方法,这个parse方法其实就是递归调用ConfigurationClassParser的processConfigurationClass方法,根据获得的bean定义再去进行配置类的解析了

处理@Import注解
下面是
doProcessConfigurationClass方法对@Import注解的处理,getImports(sourceClass)会获取@Import的value值,即要被导入的类,然后方法内会进行导入处理

进入方法内,该方法会遍历每一个被
@Import导入的类,主要处理逻辑为
-
如果导入类的类型是
ImportSelector, 判断它是普通的ImportSelector实现类型,还是DeferredImportSelector的实现类型,因为前者是后者的父接口。- 如果是普通的
ImportSelector类型,则获取其selectImports方法中返回的类名,通过类名去解析对应的类,这里会递归调用processImports方法,因为ImportSelector可以再导入ImportSelector类型。直到导入的是普通类和配置类:是配置类就继续递归调用doProcessConfigurationClass方法进行解析、普通类则封装为配置类并在后续直接作为Bean注册到容器中; - 如果是
DeferredImportSelector类型,则将其存入deferredImportSelectorHandler中延后进行解析,时机为其他所有配置类解析完成后(SpringBoot自动配置的实现就依赖于此接口),在ConfigurationClassParser.parse方法最后一步:
- 如果是普通的
-
如果导入类的类型是
ImportBeanDefinitionRegistrar,则将其实现方法中的BeanDefinition存入当前配置类集合importBeanDefinitionRegistrars中,等待后续所有注解配置类解析完毕之后,将bean定义注册到容器之中。-
当前配置类集合:就是
ConfigurationClass的集合类型成员变量importBeanDefinitionRegistrars:
-
后续的处理步骤在
ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法中,紧接着ConfigurationClassParser.parse的处理。位置在下图的this.reader.loadBeanDefinitions处,它会取出上面集合中的BeanDefinition进行注册。因此,ImportBeanDefinitionRegistrar类型的处理比DeferredImportSelector类型的处理更靠后:
-
-
如果导入的是普通java类和配置类:
- 配置类会递归调用
ConfigurationClassParser的doProcessConfigurationClass方法进行解析 - 普通类会被封装为一个配置类
ConfigurationClass,存入ConfigurationClassParser类的集合类型成员变量configurationClasses中,后续在注册bean定义时注册到容器中
- 配置类会递归调用
下面是
processImports方法具体的源码解析:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 检查通过@Import注解导入的类是否为空
if (importCandidates.isEmpty()) {
return;
}
// 开启了循环导入检查(checkForCircularImports为true),并且当前配置类已经在导入栈中时,代表发生了循环导入,会进行报错
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 如果没有循环导入,则将当前配置类压入导入栈importStack中
this.importStack.push(configClass);
try {
// 遍历每一个需要导入的类
for (SourceClass candidate : importCandidates) {
// 判断导入类的类型是否为ImportSelector
if (candidate.isAssignable(ImportSelector.class)) {
// 如果是,则实例化这个ImportSelector
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);
// 如果该ImportSelector有设置排除过滤,则将其与当前的排除过滤组合
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
// 组合排除过滤,根据路径排除某些类不进行导入
// exclusionFilter 默认是ConfigurationClassParser的DEFAULT_EXCLUSION_FILTER属性
// selectorFilter 是从实例化出来的ImportSelector中获取的
exclusionFilter = exclusionFilter.or(selectorFilter);
}
// 如果这个实例化出来的ImportSelector是`DeferredImportSelector`类型
if (selector instanceof DeferredImportSelector) {
// 则将配置类交给deferredImportSelectorHandler处理
// 它会将当前配置类存入一个集合中,待其他所有配置类都解析完毕后再解析当前配置类
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
// 如果不是`DeferredImportSelector`类型
else {
// 调用其实现的selectImports方法获取要导入的类路径数组
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 然后将这些类路径转换为SourceClass集合
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归调用当前方法,因为ImportSelector可以再导入ImportSelector类型,所以递归处理
// 会一直递归到ImportSelector导入的是普通类为止
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// 如果导入类的类型是ImportBeanDefinitionRegistrar
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 则实例化导入类ImportBeanDefinitionRegistrar
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);
// 将实例化后的ImportBeanDefinitionRegistrar存入当前配置类的bean定义注册列表中,等待后续处理
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 如果导入的类既不是ImportSelector,也不是DeferredImportSelector或ImportBeanDefinitionRegistrar
// 则将其作为一个普通的@Configuration配置类进行处理(
// 如果导入的是一个没有任何注解的java类,它会被包装为一个配置类
else {
// 先注册导入关系到导入栈中
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 对配置类进行处理,这里还是递归调用doProcessConfigurationClass方法
// 如果导入的是一个没有任何注解的java类,asConfigClass会将它包装为一个配置类,解析时将此类作为一个bean
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
// 最后,将当前配置类从导入栈中弹出
finally {
this.importStack.pop();
}
}
}
概括源码逻辑为:
-
判断导入类是否为
ImportSelector的实现类,如果是,则对其进行实例化:- 判断实例化后的
ImportSelector是不是DeferredImportSelector的实现- 如果是,则会将当前配置类存入一个集合中延迟处理,待其他所有配置类都解析完毕后,再解析当前配置类
- 如果不是,则获取
selectImports方法实现中所导入的类进行递归解析,因为ImportSelector可以再导入ImportSelector类型,一直递归到导入的是普通类或配置类为止
- 判断实例化后的
-
判断导入类是否为
ImportBeanDefinitionRegistrar的实现:- 如果是,将其实例化,并把实例化后的
ImportBeanDefinitionRegistrar实例存入当前配置类的集合属性中,等待后续处理
- 如果是,将其实例化,并把实例化后的
-
对于
@Import导入配置类和普通类的处理,会使用asConfigClass方法将这个该类转为配置类ConfigurationClass,再去调用ConfigurationClassParser的processConfigurationClass进行解析:public ConfigurationClass asConfigClass(ConfigurationClass importedBy) { if (this.source instanceof Class) { return new ConfigurationClass((Class<?>) this.source, importedBy); } return new ConfigurationClass((MetadataReader) this.source, importedBy); }进入
ConfigurationClass的构造方法:ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) { this.metadata = AnnotationMetadata.introspect(clazz); // resource就是导入的普通类,这里是其全路径类名 this.resource = new DescriptiveResource(clazz.getName()); // importedBy是@Import注解所在的配置类,即导入这个普通类的配置类 this.importedBy.add(importedBy); }如果是配置类,则又会进入
doProcessConfigurationClass方法进行解析;如果是没有任何注解的普通类,因为doProcessConfigurationClass方法没有对此种普通类的处理,所以会直接把它put进configurationClasses集合中等待后续处理:
处理@ImportResource注解
下面看
doProcessConfigurationClass方法对@ImportResource注解的处理。
在注解配置类上标注 @ImportResource 可以导入 xml 配置文件,处理逻辑是先获取@ImportResource的配置文件路径,后续根据路径在loadBeanDefinitions方法中进行处理。源码如下:
// ............
// 处理 @ImportResource 注解,主要用于处理传统的XML配置文件
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
// 获取@ImportResource注解的locations值,即xml配置文件路径
String[] resources = importResource.getStringArray("locations");
// 获取@ImportResource注解的读取器类,它用来读取解析指定的配置文件。未指定时有默认读取器(根据资源后缀)
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
// 解析资源路径中的占位符,如:classpath:{my.path}/config.xml,将my.path解析为具体路径
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
// 将解析后的资源路径和reader添加到配置类中,后续进行加载
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// ............
进入addImportedResource方法,实际是把资源路径和读取类存入了集合中
void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
// 一个LinkedHashMap集合
this.importedResources.put(importedResource, readerClass);
}
上面代码中是把配置文件的路径放入了集合中,而没有进行处理,真正处理的位置在
ConfigurationClassPostProcessor解析器processConfigBeanDefinitions方法中的loadBeanDefinitions(parse方法之后),与上面ImportBeanDefinitionRegistrar的处理相同
位置如下图红框处:

进入loadBeanDefinitions:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
// 此方法会加载bean定义,以及ImportResource配置文件
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
再进入loadBeanDefinitionsForConfigurationClass方法:
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// ..........
// 从前面的importedResources集合中获取ImportedResource配置文件并加载
// configClass.getImportedResources()取得就是前面的importedResources集合
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 这里取的是ImportBeanDefinitionRegistrar导入的bean定义,然后注册进容器中
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
处理@Bean注解
下面看
doProcessConfigurationClass方法对@Bean注解的处理。
获取所有标记 @Bean 注解的方法,然后存到configClass中后续由loadBeanDefinitions方法处理,跟上面的 @ImportResource 一致
// ............
// 第一步,提取所有被 @Bean 注解标记的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
// 第二步,将方法包装为BeanMethod并添加到configClass配置类的集合中,后续进行处理。
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 第三步,处理接口默认方法上的 @Bean 注解,即java8中接口的default方法
// 此方法会遍历配置类实现的所有接口,检查接口默认方法上的 @Bean 注解。
processInterfaces(configClass, sourceClass);
// ............
先看第一步,获取
@Bean注解标记方法的retrieveBeanMethodMetadata:
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
// 从来源类中获取注解元数据
AnnotationMetadata original = sourceClass.getMetadata();
// 通过元数据获取所有被@Bean注解标注的方法
Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
// 如果方法的数量大于1,并且元数据是StandardAnnotationMetadata(基于反射)类型
// 即通过反射方式获取的方法数量大于1
if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
try {
// 使用ASM重新读取类的元数据,它不加载类,只解析字节码
// 因为JVM反射不保证顺序,所以反射获取的方法顺序可能与源代码中的顺序不一致,这里使用ASM读取字节码
// 使用ASM目的是为了保证加载配置类中@Bean方法的从上到下的顺序与源文件.java 中一致
AnnotationMetadata asm = this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
// 从ASM得到的元数据中再次获取所有@Bean方法
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
// 比较ASM获取的方法集合 与 反射获取的方法集合,当ASM方法集合元素数量大于等于反射方法集合元素数量时进入if
if (asmMethods.size() >= beanMethods.size()) {
Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
// 遍历ASM方法集合(顺序为字节码中的顺序),
// 对于每一个ASM方法,如果能在在反射方法集合中查找同名的方法,则将反射方法元数据添加到selectedMethods中
// 这样操作后,selectedMethods中的方法顺序就是ASM读取的顺序(即源代码顺序),而方法本身还是反射得到的
for (MethodMetadata asmMethod : asmMethods) {
for (MethodMetadata beanMethod : beanMethods) {
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
selectedMethods.add(beanMethod);
break;
}
}
}
// 如果筛选后的方法数量与原始反射方法数量一致,则用selectedMethods替换原来的beanMethods
// 注意到这里替换后beanMethods已经是有序的了
// ASM获得的是有序的,反射获得的是无序的,上面的对比操作再加上此处的置换操作,即得到了有序的反射方法
if (selectedMethods.size() == beanMethods.size()) {
beanMethods = selectedMethods;
}
}
}
catch (IOException ex) {
logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
// No worries, let's continue with the reflection metadata we started with...
}
}
// 最后返回与java源文件方法顺序一致的方法集合
return beanMethods;
}
此方法的核心逻辑就是获取 @Bean 注解标记的的方法,但是中间使用了 ASM 来保证获取方法的顺序性。因为反射获取的方法顺序不保证与java源文件中的一致。所以这里 使用 ASM 读取字节码的目的,是为了保证加载配置类中 @Bean 方法的从上到下的顺序与源文件 .java 中一致。
然后看第二步,将获取的方法存入
ConfigurationClass内的集合
将上面获取的方法存入ConfigurationClass中:
void addBeanMethod(BeanMethod method) {
this.beanMethods.add(method);
}
this.beanMethods是ConfigurationClass中的一个集合:
private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
再看第三步,对标记了
@Bean注解接口的默认方法处理
此方法会获取配置类实现的所有接口,提取接口中非抽象的@Bean方法(即java8中interface的default方法)
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// getInterfaces():该方法的主要目的是获取当前类直接实现的所有接口
for (SourceClass ifc : sourceClass.getInterfaces()) {
// 调用了第一步的处理,获取bean方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
for (MethodMetadata methodMetadata : beanMethods) {
if (!methodMetadata.isAbstract()) {
// 如果@Bean标记的是非抽象方法,将其存入configClass,这里调用的是第二步处理
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
}
// 递归调用,处理当前接口的父接口
processInterfaces(configClass, ifc);
}
}
处理父类配置
如果配置类存在父类的话,父类也应该一起加载,所以这里会取到配置类的父类,并继续递归处理。
// ............
// 处理配置类的父类,确保继承中的配置也被处理
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
// 跳过父类为空的、以及Java标准库中的类(java.开头的包)不处理
// knownSuperclasses用于避免死循环解析,首次遇到父类解析后存入缓存,再次遇到时跳过避免死循环解析
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
// 将父类全限定名与当前配置类关联存入缓存。
this.knownSuperclasses.put(superclass, configClass);
// 继续向上递归处理,当前类有父类,父类向上还有更多父类时依次向上处理,将所有注解配置进行合并
return sourceClass.getSuperClass();
}
}
// ............
此部分最后的返回递归处理,联动的是doProcessConfigurationClass的while循环部分,在ConfigurationClassParser中processConfigurationClass方法如下位置:
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
//............
do {
// 如果返回了父配置类,这里循环向上处理,直到所有配置类都处理完,sourceClass为null时结束
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
//............
}
解析结果存放
回顾上面那些注解的处理:
@Component注解:针对内部配置类的递归再解析,实际就是获取内部类再去做其他注解的解析@PropertySource注解:将注解中的资源文件添加进Environment中@ComponentScan注解:根据注解中的包路径进行扫描,将扫描到的组件注册到容器中,并对其中的配置进行解析(实际上又是处理这几个注解)@Import注解:继续对导入的类进行解析,只有导入的是ImportBeanDefinitionRegistrar类型时会存入ConfigurationClass类中的集合等待注册 (导入的普通java类会直接转为ConfigurationClass作为Bean进行注册,不会放入这个集合,下面章节会介绍)@ImportResource注解:将注解中解析后的资源文件路径存入集合中等待后续处理@Bean注解:获取所有标记@Bean注解的方法,然后存到ConfigurationClass类中的集合中等待后续处理
可以看到:
@PropertySource、@ComponentScan可以直接处理,就是将资源添加到环境中,把组件注册到容器里- 而
@Import的ImportBeanDefinitionRegistrar、@ImportResource、@Bean的处理都会存入集合进行等待处理
下面介绍这些等待处理的内容是如何被处理的
doProcessConfigurationClass方法在解析注解配置类时,针对上面三个需要等待处理的内容,会将其解析结果添加到ConfigurationClass的集合中
对@ImportResource与@Bean的处理:

对ImportBeanDefinitionRegistrar的处理:

上面的三个add方法,就是把ImportBeanDefinitionRegistrar、@ImportResource、@Bean 的三项解析结果存到了ConfigurationClass的如下三个集合中:

而在
ConfigurationClassParser类调用doProcessConfigurationClass方法解析完配置类后,会把ConfigurationClass存入configurationClasses集合里:
即configurationClasses集合中包含了ConfigurationClass,而ConfigurationClass对象里含有上面三个集合,这三个集合保存了ImportBeanDefinitionRegistrar、@ImportResource、@Bean 的解析结果:

后续在
ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法中加载Bean定义时,会根据解析后的结果来进行加载:
- 实际就是通过取
configurationClasses集合,进而取到ConfigurationClass对象中的那三个集合,它们保存了针对ImportBeanDefinitionRegistrar、@ImportResource、@Bean三项配置解析的结果
如下图:
- Set集合
configClasses其实就是configurationClasses集合,通过parser.getConfigurationClasses()方法获取。然后它会作为参数被传入加载Bean定义的方法loadBeanDefinitions中

注册BeanDefinition
将配置类的解析结果加载到容器中
回到
ConfigurationClassPostProcessor中,解析完配置类后还要进行BeanDefinition的注册,而这个方法的核心在loadBeanDefinitions中:
处理源码如下,在ConfigurationClassPostProcessor的processConfigBeanDefinitions方法中

this.reader.loadBeanDefinitions(configClasses) 的执行会来到 ConfigurationClassBeanDefinitionReader 类中:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
//循环处理配置类解析结果
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
进入上面for循环的
loadBeanDefinitionsForConfigurationClass中:
注意看对ImportBeanDefinitionRegistrar、@ImportResource、@Bean 的处理,处理的参数来源就是上面ConfigurationClass类中的三个集合
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 与条件装配有关,检查配置类上是否存在@Conditional条件,且不满足。
if (trackedConditionEvaluator.shouldSkip(configClass)) {
// 不满足条件进入下面处理
// 获取配置类名
String beanName = configClass.getBeanName();
// 检查该配置类自身是否已经被注册为Bean(配置类本身也是一个Bean),如果有,则从BeanFactory中移除该Bean定义
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
// 同时移除该配置类的导入记录(因为整个配置类被跳过了,所以它不应该再被当作一个引入类)
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 第一步,@Import的处理
// 如果当前配置类是通过@Import导入的
if (configClass.isImported()) {
// 那么需要将当前配置类自身也变为一个Bean定义,并注册进容器中
// 被@Import导入的普通java类也会被封装为一个configClass从这里注册进容器中
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 第二步,注册@Bean注解方法
// 循环配置类中所有的@Bean方法,这里的getBeanMethods()就是取上面那三个集合中的一个,存放Bean方法的那一个
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 第三步,处理@ImportResource导入的XML配置文件
// getImportedResources()就是取上面那三个集合中的一个,存放ImportResource资源的那一个
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 第四步, 注册来自ImportBeanDefinitionRegistrar导入的bean
// 也是取上面那三个集合中的一个
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
下面分别介绍
loadBeanDefinitionsForConfigurationClass方法的4步处理。处理核心都是构建BeanDefinition,然后将其注册到容器中:
-
第一部分
registerBeanDefinitionForImportedConfigurationClass,作用是将配置类自身、以及被@Import注解导入的普通java类注册进BeanFactory。- 对于
@Configuration配置类,它必然也是@Component,它会被自动注册到BeanFactory中。但如果配置类是通过@Import的方式导入的,就不会被主动注册进BeanFactory,所以这里需要将那些被@Import进去的配置类,也全部注册到BeanFactory中 - 对于导入的无注解普通java类,在前面的递归解析中并没有对应的处理,而是直接封装为
ConfigurationClass,然后作为参数传入此方法,最终在此方法内构建为BeanDefinition注册到容器中
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) { // 获取配置类的注解元数据 AnnotationMetadata metadata = configClass.getMetadata(); // 包装注解元数据为AnnotatedGenericBeanDefinition,它是一个可以直接通过注解元数据构建的BeanDefinition AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); // 解析当前配置类的作用域(@Scope注解),如果没有指定,则默认为单例。 ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef); // 将解析得到的作用域设置到BeanDefinition中 configBeanDef.setScope(scopeMetadata.getScopeName()); // 生成Bean的唯一名称 // importBeanNameGenerator是一个专门用于`导入Bean`的命名生成器:如果有@Component("name")则直接使用;没有就使用类名的小写开头 String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry); // 处理类上的通用注解,如@Lazy, @Primary, @DependsOn, @Role, @Description等 // 然后将这些注解的信息设置到BeanDefinition中 AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata); // 通过BeanDefinition和Bean名称创建一个BeanDefinition持有者 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName); // 对非单例作用域 Bean 生成代理对象 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 对最终的BeanDefinition进行注册,默认注册到的容器是DefaultListableBeanFactory this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition()); // 回写配置类的Bean名称。将生成的Bean名称设置回ConfigurationClass对象,以便后续使用 configClass.setBeanName(configBeanName); if (logger.isTraceEnabled()) { logger.trace("Registered bean definition for imported class '" + configBeanName + "'"); } } - 对于
-
第二部分
loadBeanDefinitionsForBeanMethod,处理的是通过配置类解析的到的@Bean方法,通过@Bean方法获取Bean定义,将这些Bean定义注册到容器中。主要逻辑是根据@Bean注解中的各项属性及方法信息来封装对应的bean定义并注册到容器中 。下面是处理源码:private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { ConfigurationClass configClass = beanMethod.getConfigurationClass(); MethodMetadata metadata = beanMethod.getMetadata(); String methodName = metadata.getMethodName(); // 检查配置类上是否存在@Conditional条件,且是否满足。如不满足此处为true,即跳过当前bean方法的注册 if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) { // 如果跳过,将其记录到配置类的skippedBeanMethods中,并返回 configClass.skippedBeanMethods.add(methodName); return; } // 如果该方法已被跳过(上面的记录),则直接返回 if (configClass.skippedBeanMethods.contains(methodName)) { return; } // 获取 @Bean 注解的属性 AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class); Assert.state(bean != null, "No @Bean annotation attributes"); // 从@Bean注解的name属性中获取名称和别名 List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name"))); // name属性不为空,取第一个名称作为bean名,;如果为空,则取方法名作为bean名 String beanName = (!names.isEmpty() ? names.remove(0) : methodName); // 在上面取出一个name作为bean名后,把剩下的名称作为别名进行注册 for (String alias : names) { this.registry.registerAlias(beanName, alias); } // 如果存在与当前bean同名的bean定义 if (isOverriddenByExistingDefinition(beanMethod, beanName)) { // 如果当前bean的名称与配置类名一致(即配置类自身Bean名与@Bean方法名冲突),抛出异常 if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) { throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(), beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() + "' clashes with bean name for containing configuration class; please make those names unique!"); } // 存在与当前bean同名的bean定义,且bean名与配置类名不冲突,跳过此bean的处理 return; } // 根据当前bean方法信息,创建出对应的bean定义 ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName); beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource())); // 如果当前bean方法是静态的,配置静态@Bean方法的工厂信息。静态方法不依赖于配置类实例,可以直接通过类调用 if (metadata.isStatic()) { // 如果配置类的元数据是 StandardAnnotationMetadata,直接获取其类对象并设置 if (configClass.getMetadata() instanceof StandardAnnotationMetadata) { beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass()); } // 否则取配置类的类名进行设置 else { beanDef.setBeanClassName(configClass.getMetadata().getClassName()); } // 设置工厂方法名,即指定用于创建Bean的静态方法名称 beanDef.setUniqueFactoryMethodName(methodName); } // 如果是非静态的实例方法,则根据配置类对象去设置。因为实例方法依赖于配置类实例,需要通过配置类实例调用 else { // 指定包含工厂方法的 Bean(即配置类本身) beanDef.setFactoryBeanName(configClass.getBeanName()); // 指定用于创建 Bean 的实例方法名称 beanDef.setUniqueFactoryMethodName(methodName); } if (metadata instanceof StandardMethodMetadata) { beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod()); } // 设置默认自动装配模式为构造器注入 beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); // 设置跳过@Required检查。因为@Bean方法由容器管理,不需要该检查 beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor. SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); // 处理通用注解,如@Lazy, @Primary, @DependsOn, @Role, @Description等 AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata); // 根据@Bean注解属性,设置自动装配模式 Autowire autowire = bean.getEnum("autowire"); if (autowire.isAutowire()) { beanDef.setAutowireMode(autowire.value()); } // 设置当前bean是否可作为自动注入候选对象 // 用于表示该Bean是否可以被自动注入到其他需要该类型依赖的Bean中 boolean autowireCandidate = bean.getBoolean("autowireCandidate"); if (!autowireCandidate) { beanDef.setAutowireCandidate(false); } // 设置bean的生命周期初始化与销毁方法 String initMethodName = bean.getString("initMethod"); if (StringUtils.hasText(initMethodName)) { beanDef.setInitMethodName(initMethodName); } String destroyMethodName = bean.getString("destroyMethod"); beanDef.setDestroyMethodName(destroyMethodName); // 解析@Scope注解,设置作用域 ScopedProxyMode proxyMode = ScopedProxyMode.NO; AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class); if (attributes != null) { beanDef.setScope(attributes.getString("value")); // 如果代理模式是默认的,则设置为默认值 proxyMode = attributes.getEnum("proxyMode"); if (proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = ScopedProxyMode.NO; } } // 设置代理模式 BeanDefinition beanDefToRegister = beanDef; // 如果有代理模式,则根据设置的代理模式创建一个bean定义,并替换原始bean定义 if (proxyMode != ScopedProxyMode.NO) { BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy( new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS); beanDefToRegister = new ConfigurationClassBeanDefinition( (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName); } if (logger.isTraceEnabled()) { logger.trace(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getMetadata().getClassName(), beanName)); } // 最后注册bean定义 this.registry.registerBeanDefinition(beanName, beanDefToRegister); } -
第三部分
loadBeanDefinitionsFromImportedResources,解析从@ImportResource上取到的配置文件的路径,会使用XmlBeanDefinitionReader读取xml文件并将内容构建为BeanDefinition注册到容器中:// 方法参数importedResources的key是资源位置,也就是配置文件路径;value是用于读取该资源的读取器 private void loadBeanDefinitionsFromImportedResources( Map<String, Class<? extends BeanDefinitionReader>> importedResources) { // 用于存放已经实例化的BeanDefinitionReader读取器 // key是BeanDefinitionReader的Class对象,值是对应的实例。 // 这样,如果同一个Reader类被多个资源使用,便可以复用同一个实例,避免重复创建。 Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>(); // 对导入的每个资源进行遍历处理 importedResources.forEach((resource, readerClass) -> { // 当读取类的类型是BeanDefinitionReader时,判断资源文件的类型 if (BeanDefinitionReader.class == readerClass) { // 资源路径以.groovy结尾,则使用GroovyBeanDefinitionReader if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) { readerClass = GroovyBeanDefinitionReader.class; } // 如果shouldIgnoreXml为true,表示禁用XML配置,则抛出异常 else if (shouldIgnoreXml) { throw new UnsupportedOperationException("XML support disabled"); } // 默认使用XmlBeanDefinitionReader,即读取xml else { readerClass = XmlBeanDefinitionReader.class; } } // 先尝试从缓存中获取已存在的BeanDefinitionReader实例 BeanDefinitionReader reader = readerInstanceCache.get(readerClass); // 如果缓存中没有,则进行创建 if (reader == null) { try { // 通过反射创建新的实例。使用构造器创建 // BeanDefinitionReader的实现类必须有一个接受BeanDefinitionRegistry参数的构造器才可以 reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry); // 如果reader是AbstractBeanDefinitionReader的子类 if (reader instanceof AbstractBeanDefinitionReader) { AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader); // 设置ResourceLoader和Environment,这样reader就可以解析资源路径和属性占位符 abdr.setResourceLoader(this.resourceLoader); abdr.setEnvironment(this.environment); } // 将新创建的reader放入缓存,用于后续使用 readerInstanceCache.put(readerClass, reader); } catch (Throwable ex) { throw new IllegalStateException( "Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]"); } } // 加载指定资源路径的bean定义,这里传入的resource是资源路径字符串 // 对于默认的xml文件,此方法的逻辑为: // 1.将字符串资源路径转换为具体的Resource对象。 // 2.将Resource加载为Document对象。 // 3.遍历Document中的元素,解析成BeanDefinition。 // 4.将BeanDefinition注册到BeanDefinitionRegistry。 reader.loadBeanDefinitions(resource); }); } -
最后一部分是执行
ImportBeanDefinitionRegistrar接口的bean定义注册 ,这里直接调用其registerBeanDefinitions方法,将导入的bean定义注册到容器中:private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> // 将导入的bean定义注册到容器中 registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator)); }
BeanDefinition的存储
经过上面的步骤进行处理后,配置类已经被解析为bean定义注册到容器中,这里看bean定义也就是
BeanDefinition在容器中的存储位置:
会将BeanDefinition与bean名称存入容器(默认是DefaultListableBeanFactory)的两个集合中:
// 这两个集合都在容器实现类`DefaultListableBeanFactory`中
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
后续实例化bean过程中,会在合并bean定义时从beanDefinitionNames集合中取出bean名称,再用bean名称去beanDefinitionMap集合中取出BeanDefinition,在根据BeanDefinition去进行Bean的实例化。
DeferredImportSelector原理
在解析配置类,调用
ConfigurationClassParser的parse方法中,可以看到@Import注解的一个特殊情况:
如果它导入的是DeferredImportSelector类型,会先将其存入deferredImportSelectorHandler中,待后续配置类都解析完毕后在进行解析,从而实现DeferredImportSelector的延迟处理:

进入上图解析注解配置类的parse方法,如下图,可以看到方法内将DeferredImportSelector存入了deferredImportSelectorHandler中,这个deferredImportSelectorHandler就是上图中的deferredImportSelectorHandler:

进入上面执行
DeferredImportSelector的DeferredImportSelectorHandler.process方法
public void process() {
// 获取所有待处理的DeferredImportSelector,给到一个新集合中
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
// 将原集合置空,确保这些延迟导入只被处理一次
this.deferredImportSelectors = null;
try {
// 如果待处理的DeferredImportSelector不为空
if (deferredImports != null) {
// 创建分组实例,在下面进行分组处理
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
// 排序
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 进行分组
deferredImports.forEach(handler::register);
// 按分组执行导入,这里是递归调用processImports方法,具体逻辑看上面`处理@Import注解`的章节
handler.processGroupImports();
}
}
// 确保上面无论是否出现异常都置空this.deferredImportSelectors
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
它会取出所有存储好的 DeferredImportSelector ,并依次执行解析。由于 DeferredImportSelector 的执行时机比较晚,对于 @Conditional 条件装配的处理也会更有利。
SpringBoot自动装配利用了这个机制:
@SpringBootApplication注解内的子注解@EnableAutoConfiguration,会导入DeferredImportSelector的实现类AutoConfigurationImportSelector,它会利用Spring的SPI机制从spring.factories文件中获取自动配置类并进行加载。正是这个机制,SpringBoot的自动配置才会被延迟到所有用户定义配置处理完毕之后再处理。
622

被折叠的 条评论
为什么被折叠?



