注解解析原理:ConfigurationClassPostProcessor及ConfigurationClassParser

本文详细介绍了Spring框架中注解配置的处理流程,包括AnnotationConfigUtils如何注册处理配置的BeanDefinition,ConfigurationClassPostProcessor的工作原理及如何解析@PropertySource、@ComponentScan、@Import等注解。

1. 知识准备:

1.1 AnnotationConfigUtils

在读取beanDefination的时候,调用自定义的解析器,对于AnnotationConfigBeanDefinitionParser和ComponentScanBeanDefinitionParser会调用AnnotionCOnfigUtils.java 的registerAnnotationConfigProcessors,把beanName注册进去。

//==================
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor...


AnnotationConfigUtils.java
{
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =			"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
   .. ..
 // 注册内部管理的用于处理@configuration注解的后置处理器的bean
//****************重点*****************
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(..){
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			// 注册BeanDefinition到注册表中  。ConfigurationClassPostProcessor到注册表中(以后可以实例化了)
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
    
   .....       
	}    
}   
1.2 调用时机

其中第一和第二,是处理xml配置(component-scan和annotation-config)的时候,注册bfpp等定义信息的。第三种是注解启动时候注册各种bd信息的。

==========================注解工具注册====================
    
 调用registerAnnotationConfigProcessors(..)注册bd
// 根据条件,选择注册bean的定义信息
1. ConfigurationClassPostProcessor(bfpp) 
2. AutowiredAnnotationBeanPostProcessor(bpp)
3. CommonAnnotationBeanPostProcessor(bpp)
4. EventListenerMethodProcessor(bfpp)   
5. DefaultEventListenerFactory 等
    
在下面三个地方调用了:    
1. 
//registerBeanDefinitionParser("annotation-config", new //AnnotationConfigBeanDefinitionParser());
    
AnnotationConfigBeanDefinitionParser.java#parse{
    //注册注解配置处理器 bean信息
Set<BeanDefinitionHolder> processorDefinitions =
	//注册bfpp和bpp
  AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);   
}
//根据标签解析。注册解析器
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());



2.
//处理 <component:scan base-package.. 等xml 配置
// registerBeanDefinitionParser("component-scan", new //ComponentScanBeanDefinitionParser());
    
ComponentScanBeanDefinitionParser.java#parse{
    
    ClassPathBeanDefinitionScanner#doScan(); 
    // 把扫描到的bd放进容器 
    // 注册系统bd信息
    AnnotationConfigUtils.registerAnnotationConfigProcessors(..)
  
  } 

3.本类 registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
		registerAnnotationConfigProcessors(registry, null);
}
3.1 使用注解启动的时候,使用的。AnnotationConfigApplicationContext启动的
    //在构造方法中调用了
   	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
      // 。。。
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

2. 源码分析

2.1 开始

refresh()

-> invokeBeanFactoryPostProcessors

  // 找到所有实现BeanDefinitionRegistryPostProcessor接口bean的beanName
    String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

//实例化bfpp
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
...
//调用
 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
 -> 
PostProcessorRegistrationDelegate.java    	 postProcessor.postProcessBeanDefinitionRegistry(registry);

到达ConfigurationClassPostProcessor.java

	 // 定位、加载、解析、注册相关注解
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		//....
		processConfigBeanDefinitions(registry);
	}

到达ConfigurationClassPostProcessor.java

/**
	 * 构建和验证一个类是否被@Configuration修饰,并做相关的解析工作
	 *   获取所有的beanDefination对象,然后遍历,查看这些类上面的注解
	 */
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		String[] candidateNames = registry.getBeanDefinitionNames();
		// 遍历所有要处理的beanDefinition的名称,筛选对应的beanDefinition
        //被@Component已经注册过了
		for (String beanName : candidateNames) {
		do {
			// 解析带有@Controller、@Import、@ComponentScan、@Bean的BeanDefinition
			// ConfigurationClassParser parser 
			parser.parse(candidates);

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			// 将完全填充好的ConfigurationClass实例转化为BeanDefinition注册入IOC容器
			this.reader.loadBeanDefinitions(configClasses);
			}
	}

转移到ConfigurationClassParser.java类。核心处理方法在doProcessConfigurationClass()中。

=======>ConfigurationClassParser.java#parse(..){
 //根据不同的类型,调用重载的parse方法
}
以注解为例子
->parse()
-->processConfigurationClass()
--->doProcessConfigurationClass(){
	//1. 递归处理内部类,符合条件的加入到候选。遍历处理
	2. 处理@PropertySource 注解
	3. 处理处理@ComponentScan或者@ComponentScans注解,通过ComponentScanAnnotationParser#parse(..),进而调用ClassPathBeanDefinitionScanner#doScan(..),解析的是@Component及其子类,解析到了所有的scannedBeanDefinitions,判断是,是不是配置类,是配置类,再递归处理
	4. 处理@Import注解
	5. 处理@ImportResource注解
	6. 处理@Bean注解的方法
	7. 处理接口,jdk8支持接口@Bean注解
}
// 到此,注解解析完毕
2.2 源码流程

AbstractApplicationContext.java#invokeBeanDefinitionRegistryPostProcessors
-》PostProcessorRegistrationDelegate.java#invokeBeanDefinitionRegistryPostProcessors
-》PostProcessorRegistrationDelegate.java#postProcessor(ConfiguratiionClassPostProcessor).postProcessBeanDefinitionRegistry(registry);
-》ConfiguratiionClassPostProcessor.java#processConfigBeanDefinitions(registry);
-》ConfiguratiionClassPostProcessor.java#parser.parse(candidates);这里的parser就是ConfigurationClassParser类型,解析完,从列表bd放到工厂中;
-》ConfigurationClassParser.java#processConfigurationClass(…);这里放到列表中
-》ConfigurationClassParser.java#doProcessConfigurationClass(…),这里开始解析各种注解,然后configurationClasses.put(configClass, configClass);这里是将
@Import注解标记的普通类,放到列表,@ComponentScan扫描到的被标记的,然后调用processConfigurationClass()
-》

<think>我们正在讨论SpringBoot中的ConfigurationClassPostProcessor。根据用户的问题,他想了解这个类的具体作用和工作原理。根据引用[1][2][3][4],我们可以知道SpringBoot的启动过程涉及多个核心组件,其中ConfigurationClassPostProcessorSpring框架中处理配置类的关键后置处理器。作用:ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor的实现类,它在Spring容器启动过程中负责解析配置类(比如带有@Configuration注解的类)并处理相关的注解(如@ComponentScan,@Import,@Bean等),从而将更多的bean定义注册到容器中。工作原理:1.在Spring容器刷新(refresh)阶段,会调用所有BeanFactoryPostProcessor的postProcessBeanFactory方法,以及BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法(因为ConfigurationClassPostProcessor实现了这个接口)。2.ConfigurationClassPostProcessor首先会查找当前容器中所有的BeanDefinition,然后筛选出配置类(配置类通常带有@Configuration注解,但也可以是@Component、@ComponentScan、@Import、@ImportResource注解修饰的类,或者是任何有@Bean方法的类)。3.然后使用ConfigurationClassParser解析这些配置类,解析过程包括:-解析@ComponentScan注解,扫描指定包下的组件并注册为BeanDefinition。-解析@Import注解,导入其他配置类或ImportSelector、ImportBeanDefinitionRegistrar的实现类。-解析@ImportResource注解,导入XML配置文件。-解析@Bean注解的方法,将这些方法注册为BeanDefinition。-处理配置类中嵌套的配置类(内部类)。4.解析完成后,ConfigurationClassPostProcessor会使用ConfigurationClassBeanDefinitionReader将解析得到的配置类中定义的bean(例如通过@Bean方法、@Import导入的组件等)注册到容器中。值得注意的是,ConfigurationClassPostProcessorSpring框架的一部分,并不是SpringBoot特有的,但SpringBoot大量使用配置类(如自动配置)所以它非常重要。引用[2]中提到SpringBoot的自动配置也是通过@EnableAutoConfiguration注解(通过@Import导入AutoConfigurationImportSelector)来实现的,而这个处理过程就是由ConfigurationClassPostProcessor来完成的。另外,引用[4]中提到,SpringBoot在启动时读取spring.factories文件中的配置类并不是直接通过@SpringBootApplication注解,而是通过SpringFactoriesLoader加载,然后由ConfigurationClassPostProcessor来处理这些配置类。因此,ConfigurationClassPostProcessorSpringBoot启动过程中扮演了核心角色,它负责处理所有配置类,包括自动配置类的导入和解析。下面我们来详细说明:步骤1:在SpringApplication.run()方法启动后,会创建AnnotationConfigApplicationContext(或者其它类型的上下文),然后会调用refresh()方法。步骤2:在refresh()方法中,会调用invokeBeanFactoryPostProcessors(beanFactory)方法,这个方法会执行所有BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor。步骤3:ConfigurationClassPostProcessor作为BeanDefinitionRegistryPostProcessor,它的postProcessBeanDefinitionRegistry方法会被调用。在这个方法中,它会开始处理配置类。具体源码层面(简要):-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry->processConfigBeanDefinitions(registry)->创建ConfigurationClassParser解析配置类(parse()方法)->创建ConfigurationClassBeanDefinitionReader,读取配置类并注册bean定义(loadBeanDefinitions()方法)在解析过程中,如果遇到@EnableAutoConfiguration,那么AutoConfigurationImportSelector会被调用,它会从spring.factories中加载自动配置类。总结:ConfigurationClassPostProcessor的作用是解析配置类并注册相关的bean定义。它是Spring容器中处理Java配置的核心处理器,也是SpringBoot自动配置机制能够生效的关键。相关问题:1.ConfigurationClassPostProcessorSpring容器启动过程中的哪个阶段被调用?2.如何自定义一个配置类?配置类中常用的注解有哪些?3.SpringBoot的自动配置是如何通过ConfigurationClassPostProcessor实现的?4.除了@Configuration,还有哪些注解可以标记一个类为配置类?注意:在回答中,我们引用了[1][2][3][4],但用户的问题主要集中在ConfigurationClassPostProcessor,所以我们结合这些引用内容进行了解释。</think>### Spring Boot 中 `ConfigurationClassPostProcessor` 的作用和工作原理 `ConfigurationClassPostProcessor` 是 Spring 框架中处理**配置类(`@Configuration`)** 的核心后置处理器,在 Spring Boot 启动过程中扮演关键角色。其作用和工作原理如下: --- #### **一、核心作用** 1. **解析配置类** 扫描并处理所有被 `@Configuration` 注解标记的类(包括 `@SpringBootApplication` 隐含的配置类),解析其中的: - `@Bean` 方法 → 注册 Bean 定义 - `@ComponentScan` → 扫描指定包下的组件 - `@Import` → 导入其他配置类或 `ImportSelector`/`ImportBeanDefinitionRegistrar` - `@PropertySource` → 加载外部属性文件 2. **实现自动配置的关键** 通过解析 `@EnableAutoConfiguration`(内置于 `@SpringBootApplication`)触发的 `AutoConfigurationImportSelector`,加载 `META-INF/spring.factories` 中的自动配置类[^2][^4]。 3. **注册 Bean 定义** 将解析结果转换为 Spring 容器的 `BeanDefinition`,为后续 Bean 实例化做准备。 --- #### **二、工作原理(源码级流程)** ##### **阶段 1:配置类发现** - Spring Boot 启动时,`SpringApplication.run()` 初始化 `AnnotationConfigApplicationContext`。 - 容器刷新阶段(`refresh()` 方法)调用 `BeanFactoryPostProcessor`,其中 `ConfigurationClassPostProcessor` 作为 `BeanDefinitionRegistryPostProcessor` 优先执行[^1][^2]。 ##### **阶段 2:配置类解析** ```java // 简化的源码流程 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // 1. 筛选候选配置类(含 @Configuration 的类) List<BeanDefinition> configCandidates = findConfigCandidates(registry); // 2. 使用 ConfigurationClassParser 解析配置类 ConfigurationClassParser parser = new ConfigurationClassParser(); parser.parse(configCandidates); // 解析内部注解 // 3. 处理 @Import(AutoConfigurationImportSelector.class) if (注解含 @EnableAutoConfiguration) { loadAutoConfigurations(parser); // 加载 spring.factories 中的配置类 } } ``` ##### **阶段 3:Bean 定义注册** - 通过 `ConfigurationClassBeanDefinitionReader` 将解析结果注册到容器: ```java // 注册 @Bean 方法、@Imported 资源等 reader.loadBeanDefinitions(configClasses); ``` - 处理嵌套配置类(内部类)和 `@Profile` 条件注解。 ##### **阶段 4:代理增强(CGLIB)** - 为 `@Configuration` 类生成 CGLIB 代理,确保 `@Bean` 方法调用时返回单例 Bean(避免重复创建)[^3]。 --- #### **三、与 Spring Boot 自动配置的关系** 1. `@SpringBootApplication` 组合了 `@EnableAutoConfiguration`。 2. `ConfigurationClassPostProcessor` 解析注解时,触发 `AutoConfigurationImportSelector`: ```java // AutoConfigurationImportSelector 关键逻辑 List<String> loadFactoryNames(Class<?> factoryType) { return SpringFactoriesLoader.loadFactoryNames(factoryType, classLoader); } ``` 从 `META-INF/spring.factories` 加载自动配置类(如 `DataSourceAutoConfiguration`)[^2][^4]。 3. 最终将这些配置类解析为 `BeanDefinition` 并注册到容器。 --- #### **四、总结流程** $$ \text{启动类} \xrightarrow{\text{@SpringBootApplication}} \text{容器初始化} \xrightarrow{\text{ConfigurationClassPostProcessor}} \text{解析配置类} \xrightarrow{\text{加载自动配置}} \text{注册 Bean} $$ 该处理器是 Spring Boot **“约定优于配置”** 能力的核心实现者,将 Java 配置转换为运行时 Bean 结构[^1][^3]。 --- ### 相关问题 1. **`ConfigurationClassPostProcessor` 在 Spring 容器刷新流程中的哪个阶段执行?** 2. **如何自定义一个配置类并确保它被 `ConfigurationClassPostProcessor` 处理?** 3. **为什么 `@Configuration` 类需要 CGLIB 代理?能否禁用代理?** 4. **Spring Boot 的 `spring.factories` 机制与 `ConfigurationClassPostProcessor` 如何协作?** [^1]: Spring Boot 启动原理完整解析 [^2]: Spring Boot 自动配置实现原理(源码分析) [^3]: Spring Boot 属性加载原理解析 [^4]: `@SpringBootApplication` 与 `spring.factories` 的交互逻辑
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值