Bean生命周期BeanDefinition阶段

Spring中,Bean的生命周期历经以下几个阶段:

  1. BeanDefinition阶段: 即Bean定义阶段。在bean被创建之前,bean是以 BeanDefinition 的方式被描述和注册在BeanFactory中的。此阶段通过解析配置获得BeanDefinition,然后将其存储到容器中用于后续处理
  2. Bean实例化阶段: 借助解析配置类后得到的 BeanDefinition (即Bean定义),对Bean进行实例化,创建出bean对象
  3. Bean属性注入阶段: 此阶段前,会先收集Bean的初始化销毁依赖注入相关的元数据信息,然后根据元数据对Bean中需注入的属性或方法执行依赖注入
  4. Bean初始化阶段: 对Bean进行初始化,按照顺序执行初始化方法,顺序为先执行@PostConstruct注解标记的方法、然后是实现InitializingBean接口重写的afterPropertiesSet方法、最后是init-method方法
  5. Bean销毁阶段:销毁当前Bean,按顺序执行其销毁方法,顺序为执行@PreDestroy注解标记的方法、然后是实现DisposableBean接口重写的destroy方法、最后是destroy-method方法

其他阶段的解析参考:

简介

本文介绍Bean定义阶段,主要流程是:通过解析配置类来获得Bean定义,并将Bean定义注册到容器中,后续根据Bean定义来对Bean进行实例化、属性注入、初始化等操作。

由于xml的配置已不常用,下面对注解配置类的解析过程进行简单概括:

注解配置类的解析发生在容器刷新时( 主启动类run方法中 refreshContext方法)的BeanDefinitionRegistryPostProcessor 执行阶段。BeanDefinitionRegistryPostProcessor是一个后置处理器,它执行时会获取一个自己的实现类 ConfigurationClassPostProcessor ,该实现用于解析配置类,其主要负责的工作分为两步:

  1. 解析配置类:创建一个ConfigurationClassParser来解析配置类,这里的配置类指的不光是@Configuration类,还包括@Component@Import@PropertySource
  2. 注册Bean定义:将配置类的解析结果构建为 BeanDefinition,并注册到容器中 ,后续Spring会通过 BeanDefinition 来对Bean进行实例化、属性注入、初始化等操作

第一步,解析注解配置类的具体过程

源码中处理的是如下几个注解:

  1. 处理@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;
      
      }
      
  2. 处理@PropertySource 注解。主要是根据此注解中的路径,来加载对应的资源文件,将其添加进Environment中(Environment包含ProfilePropertyProfile用于配置激活的环境,Property则用于从外部配置如配置文件、系统环境变量、命令行等读取属性并注入到bean组件中)

  3. 判断当前类是否有标记了@ComponentScans@ComponentScan注解。如果有则循环处理,根据注解中的路径使用扫描器ClassPathBeanDefinitionScanner进行扫描,将扫描到符合条件的类(默认被 @Component 注解标注的类)包装为 BeanDefinition并注册到容器中。如果扫描到的类中有配置类,还会通过BeanDefinition对扫描到的配置类进行递归解析

    这里的递归就是依然再执行`ConfigurationClassParser`配置类解析器的解析方法,解析扫描到的配置类中的`@Component``@Bean``@Import``@PropertySource``@ImportResource`等注解
    
  4. 处理@Import注解,将此注解导入的类解析为BeanDefinition并注册到容器中。它能导入的值有4种类型:

    • ImportSelector类型:此类型的selectImports方法会返回类名,通过类名将相应的类解析为bean,通常使用中都是返回配置类和普通类的类名。如果是配置类则还是ConfigurationClassParser递归解析;普通类会转为配置类进行处理,并在后续注册bean定义时注册到容器中
    • DeferredImportSelector类型:也是通过selectImports方法获得配置类或普通类的类名,然后通过类名将对应的类解析为bean。但此类型是延迟进行解析处理,时机在注解配置类都解析完成之后
    • ImportBeanDefinitionRegistrar类型,此类型的registerBeanDefinitions方法用于创建BeanDefinition,并将其存入bean定义注册列表中,后续在注册Bean定义步骤时处理
    • 导入普通类和配置类:配置类依然是ConfigurationClassParser递归解析;普通类会被封装为一个配置类,存入ConfigurationClassParser类的集合类型成员变量configurationClasses中,后续在注册bean定义时注册到容器中
  5. 处理@ImportResource注解,取出注解中的配置文件路径缓存起来,后续在注册Bean定义步骤时进行处理

  6. 处理@Bean注解,获取所有标记 @Bean 注解的方法,存起来后续在注册Bean定义步骤时处理,跟上面的 @ImportResource 一样。

  7. 处理配置类的父类。如果配置类存在父类的话,父类也应该一起加载,这里会递归调用注解配置类的解析方法,再次进入上面几个注解的处理,直到所有配置类都处理完毕。


第二步,注册BeanDefinition时的具体处理过程:

注册BeanDefinition时主要处理三项内容:

  1. @Import 导入的ImportBeanDefinitionRegistrar和普通类
  2. @Bean注解标记的方法
  3. @ImportResource 导入的外部配置文件

为什么只是上面三项处理? 因为除了@ComponentScans@ComponentScan有点特殊之外(直接将扫描到的类转为BeanDefinition 注册到容器中),只有上面三项是产生了解析结果的:

  1. 当配置类标记的是@Component注解时,只是递归处理其内部类,相当于重新进入了@Component@PropertySource@ComponentScans@ComponentScan@Import@ImportResource@Bean这些注解的处理流程,并不是标记了@Component就直接解析为BeanDefinition
  2. @PropertySource 注解是将配置文件作为属性源添加到环境中,它主要是修改BeanDefinition而不是创建。应用此注解后底层会通过PropertySourcesPlaceholderConfigurer解析BeanDefinition中的占位符,并使用属性源中的属性替换BeanDefinition中对应的属性
  3. @ComponentScans@ComponentScan将扫描到的类转为BeanDefinition 存入容器中之后,还会通过这些BeanDefinition 重新进入@Component@PropertySource@ComponentScans@ComponentScan@Import@ImportResource@Bean这些注解的处理循环,因为扫描到的类可能也是配置类
  4. 如果@Import导入的是非ImportBeanDefinitionRegistrar类型,其实还是递归进行@Component@PropertySource@ComponentScans@ComponentScan@Import@ImportResource@Bean这些注解的解析(没有任何注解的普通java类是一种特殊情况,它也可以被@Import导入,但不会被递归解析处理,源码中是将其转为配置类,然后在注册bean定义时根据类信息创建出BeanDefinition 对象进行注册)

处理过程:

  1. 针对@Import 导入的ImportBeanDefinitionRegistrar,会从其registerBeanDefinitions方法中直接取出bean定义进行注册;对于@Import 导入的配置类,将配置类本身作为bean注册进容器中;对于@Import 导入的普通类,根据类信息创建一个bean定义注册进容器中
  2. 针对@Bean注解标记的方法,主要是根据@Bean注解中的各项属性和方法信息来封装对应的BeanDefinition并注册到容器中
  3. 针对@ImportResource 导入的外部配置文件,解析从@ImportResource上取到的配置文件的路径,读取其内容解析为BeanDefinition并注册到容器中。日常使用较多是导入xml文件,对于xml文件,会使用XmlBeanDefinitionReader读取其内容构建为BeanDefinition注册到容器中
  4. bean定义注册到容器中,保存的位置在容器默认实现DefaultListableBeanFactory类的beanDefinitionMap集合中

下面根据源码进行具体的分析。


源码入口

源码版本:

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.7.10</version>
</parent>

主启动类run方法中 prepareContext 进行容器初始化

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

image-20251125135855016

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

image-20250718102840956

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

image-20250718103030859

注意:

  1. BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor ,所以也会执行BeanDefinitionRegistryPostProcessor
  2. 但是源码中,是先执行BeanDefinitionRegistryPostProcessor,然后再执行BeanFactoryPostProcessor

进入上图的invokeBeanFactoryPostProcessors()后,发现是通过代理去执行的:

image-20250718103240519

再进入代理执行的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方法的执行机制如下,顺序是:

  1. 先执行BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry方法
  2. 然后再执行BeanFactoryPostProcessorpostProcessBeanFactory方法

下面是具体执行过程,这里只需要知道配置类的解析过程发生在实现了PriorityOrdered 接口的BeanDefinitionRegistryPostProcessor 中即可:

  1. 先执行 invokeBeanFactoryPostProcessors 方法第二个参数中所包含的 BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 方法(方法参数中的后置处理器是由run方法中的prepareContext方法添加来的,位置就在refreshContext方法的上一行)
  2. 然后执行容器中实现了下列接口的 BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 方法
    • 先执行实现了 PriorityOrdered 接口的BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 方法
    • 然后执行实现了 Ordered 接口的 BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 方法
    • 最后执行除上面两种之外其他所有的 BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 方法
  3. 最后执行容器中实现了下列接口的BeanFactoryPostProcessorpostProcessBeanFactory 方法
    • 先执行实现了 PriorityOrdered 接口的BeanFactoryPostProcessorpostProcessBeanFactory 方法
    • 然后执行实现了 Ordered 接口的BeanFactoryPostProcessorpostProcessBeanFactory 方法
    • 最后执行除上面两种之外其他所有的 BeanFactoryPostProcessorpostProcessBeanFactory 方法

注解配置类解析处理器

下面看注解配置类是如何被解析的,在invokeBeanFactoryPostProcessors方法中,它会先执行所有实现了 PriorityOrdered 接口的 BeanDefinitionRegistryPostProcessor处理器,而这些处理器实现中的ConfigurationClassPostProcessor,就是用来解析注解配置类的。

为什么源码中是先执行实现了 PriorityOrdered 接口的:

  • PriorityOrdered 是 Spring 框架中一个重要的排序接口,拥有最高优先级。它的核心价值在于让关键基础组件优先初始化,比如属性占位符配置器必须在其他Bean之前处理,否则依赖注入时会找不到解析占位符的组件

Spring 的排序系统分为三个层次:

  1. PriorityOrdered 对象:最高优先级
  2. Ordered 对象:中等优先级
  3. 普通对象(未实现任何排序接口):最低优先级

方法中会先执行的处理器(也是BeanDefinitionRegistryPostProcessor的实现类)是 ConfigurationClassPostProcessor ,它主要用来解析注解配置类。触发位置如下图

invokeBeanFactoryPostProcessors方法内的invokeBeanDefinitionRegistryPostProcessors

image-20250718103819250

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

image-20250827160113493

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

image-20250718105028287

上图的ConfigurationClassPostProcessor.processConfigBeanDefinitions方法有两步重要操作:

  1. 筛选出配置类
  2. 解析筛选出的配置类

下面分别介绍


筛选配置类

进入上图的processConfigBeanDefinitions方法内,先看配置类筛选逻辑:

这里筛选出的配置类,就是标记有@Configuration@Component@ComponentScan@Import@ImportResource注解的类:

  • 第一步,先从容器中获取bean定义,因为run方法运行时调用的prepareContext 方法会把主启动类注册到容器中,所以第一处红框的candidateNames中包含主启动类

  • 第二步,checkConfigurationClassCandidate方法会检查注解,主启动类@SpringBootApplication注解中包含了@SpringBootConfiguration,后者其实就是一个@Configuration,所以主启动类的bean定义被放入了configCandidates集合中,后续的配置类解析的来源就是这个集合。

image-20251125143707293

进入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扫描到项目中的其他配置类,然后对扫描到的所有配置类进行递归解析
image-20250827104320211

进入 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,它负责实际解析配置类:

image-20250718112935341

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,再通过MutablePropertySourcesPropertySource添加到环境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 ),逻辑如下:

  1. 根据注解中的路径进行扫描,将扫描到的、符合条件的类(默认被 @Component 注解标注的类)包装为 BeanDefinition注册进容器中(bean定义保存在容器默认实现DefaultListableBeanFactorybeanDefinitionMap集合中)
  2. 完成注册后,返回扫描到的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进行解析处理,内部还是递归调用ConfigurationClassParserprocessConfigurationClass方法

下面先看扫描并返回BeanDefinition的处理:this.componentScanParser.parse

parse 方法用来处理包扫描的工作,核心逻辑就是创建一个扫描器scanner,然后获取 @ComponentScan 注解中的各项属性赋值给这个扫描器(比如根据basePackages整理出要进行包扫描的包路径,其他还有nameGeneratorincludeFiltersexcludeFilters等),然后用扫描器进行扫描,将扫描到的 BeanDefinition 注册到容器中并返回

进入这个 parse 方法,这里只看关键的最后一行:

image-20250718140616370

该扫描器是ClassPathBeanDefinitionScanner,进入其doScan方法内部,处理逻辑是:

  • 只要扫描到了符合的类(默认被 @Component 注解标注的类),就会将其包装为 BeanDefinition ,然后对这些 BeanDefinition 进行一些额外的处理(如作用域、bean名生成等),最后把 BeanDefinition 注册进容器中并返回
  • BeanDefinition 在容器中的保存位置在容器默认实现DefaultListableBeanFactorybeanDefinitionMap集合中。
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方法其实就是递归调用ConfigurationClassParserprocessConfigurationClass方法,根据获得的bean定义再去进行配置类的解析了

image-20250829162604981


处理@Import注解

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

image-20250718142003721

进入方法内,该方法会遍历每一个被@Import导入的类,主要处理逻辑为

  1. 如果导入类的类型是ImportSelector 判断它是普通的ImportSelector实现类型,还是DeferredImportSelector的实现类型,因为前者是后者的父接口。

    • 如果是普通的ImportSelector类型,则获取其selectImports方法中返回的类名,通过类名去解析对应的类,这里会递归调用processImports方法,因为ImportSelector可以再导入ImportSelector类型。直到导入的是普通类和配置类:是配置类就继续递归调用doProcessConfigurationClass方法进行解析、普通类则封装为配置类并在后续直接作为Bean注册到容器中;
    • 如果是DeferredImportSelector类型,则将其存入deferredImportSelectorHandler中延后进行解析,时机为其他所有配置类解析完成后(SpringBoot自动配置的实现就依赖于此接口),在ConfigurationClassParser.parse方法最后一步:image-20250725151830508
  2. 如果导入类的类型是ImportBeanDefinitionRegistrar,则将其实现方法中的BeanDefinition存入当前配置类集合importBeanDefinitionRegistrars 中,等待后续所有注解配置类解析完毕之后,将bean定义注册到容器之中。

    • 当前配置类集合:就是ConfigurationClass的集合类型成员变量importBeanDefinitionRegistrars

      image-20251011153231578

    • 后续的处理步骤在ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法中,紧接着ConfigurationClassParser.parse的处理。位置在下图的this.reader.loadBeanDefinitions处,它会取出上面集合中的BeanDefinition进行注册。因此,ImportBeanDefinitionRegistrar类型的处理比 DeferredImportSelector 类型的处理更靠后

      image-20250830162650123

  3. 如果导入的是普通java类和配置类

    • 配置类会递归调用ConfigurationClassParserdoProcessConfigurationClass方法进行解析
    • 普通类会被封装为一个配置类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();
		}
	}
}

概括源码逻辑为:

  1. 判断导入类是否为ImportSelector的实现类,如果是,则对其进行实例化:

    • 判断实例化后的ImportSelector是不是DeferredImportSelector的实现
      • 如果是,则会将当前配置类存入一个集合中延迟处理,待其他所有配置类都解析完毕后,再解析当前配置类
      • 如果不是,则获取selectImports方法实现中所导入的类进行递归解析,因为ImportSelector可以再导入ImportSelector类型,一直递归到导入的是普通类或配置类为止

  2. 判断导入类是否为ImportBeanDefinitionRegistrar的实现:

    • 如果是,将其实例化,并把实例化后的ImportBeanDefinitionRegistrar实例存入当前配置类的集合属性中,等待后续处理

  3. 对于@Import导入配置类和普通类的处理,会使用asConfigClass方法将这个该类转为配置类ConfigurationClass,再去调用ConfigurationClassParserprocessConfigurationClass进行解析:

    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集合中等待后续处理:

    image-20251011154159845


处理@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方法中的loadBeanDefinitionsparse方法之后),与上面ImportBeanDefinitionRegistrar的处理相同

位置如下图红框处:

image-20250723172614193

进入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.beanMethodsConfigurationClass中的一个集合:

private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();

再看第三步,对标记了@Bean注解接口的默认方法处理

此方法会获取配置类实现的所有接口,提取接口中非抽象的@Bean方法(即java8中interfacedefault方法)

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循环部分,在ConfigurationClassParserprocessConfigurationClass方法如下位置:

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类中的集合中等待后续处理

可以看到:

  1. @PropertySource@ComponentScan可以直接处理,就是将资源添加到环境中,把组件注册到容器里
  2. @ImportImportBeanDefinitionRegistrar@ImportResource@Bean 的处理都会存入集合进行等待处理

下面介绍这些等待处理的内容是如何被处理的

doProcessConfigurationClass方法在解析注解配置类时,针对上面三个需要等待处理的内容,会将其解析结果添加到ConfigurationClass的集合中

@ImportResource@Bean的处理:

image-20250901143256896

ImportBeanDefinitionRegistrar的处理:

image-20250901142902332

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

image-20250901143128617

而在ConfigurationClassParser类调用doProcessConfigurationClass方法解析完配置类后,会把ConfigurationClass存入configurationClasses集合里:

configurationClasses集合中包含了ConfigurationClass,而ConfigurationClass对象里含有上面三个集合,这三个集合保存了ImportBeanDefinitionRegistrar@ImportResource@Bean 的解析结果:

image-20250725161756769

后续在ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法中加载Bean定义时,会根据解析后的结果来进行加载:

  • 实际就是通过取configurationClasses集合,进而取到ConfigurationClass对象中的那三个集合,它们保存了针对ImportBeanDefinitionRegistrar@ImportResource@Bean 三项配置解析的结果

如下图:

  • Set集合configClasses其实就是configurationClasses集合,通过parser.getConfigurationClasses()方法获取。然后它会作为参数被传入加载Bean定义的方法loadBeanDefinitions

image-20250725162621751


注册BeanDefinition

将配置类的解析结果加载到容器中

回到 ConfigurationClassPostProcessor 中,解析完配置类后还要进行 BeanDefinition 的注册,而这个方法的核心在 loadBeanDefinitions 中:

处理源码如下,在ConfigurationClassPostProcessorprocessConfigBeanDefinitions方法中

image-20250725162621751

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,然后将其注册到容器中:

  1. 第一部分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 + "'");
    	}
    }
    
  2. 第二部分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);
    }
    
  3. 第三部分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);
    	});
    }
    
  4. 最后一部分是执行 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在容器中的存储位置:

会将BeanDefinitionbean名称存入容器(默认是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原理

在解析配置类,调用ConfigurationClassParserparse方法中,可以看到@Import注解的一个特殊情况:

如果它导入的是DeferredImportSelector类型,会先将其存入deferredImportSelectorHandler中,待后续配置类都解析完毕后在进行解析,从而实现DeferredImportSelector的延迟处理:

image-20250725151830508

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

image-20250725152349137

进入上面执行 DeferredImportSelectorDeferredImportSelectorHandler.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的自动配置才会被延迟到所有用户定义配置处理完毕之后再处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值