ConfigurationClassPostProcessor 详解
ConfigurationClassPostProcessor 是 Spring 框架中实现注解驱动配置的核心后置处理器。它负责解析所有带有 @Configuration 注解的类,处理其中的 @Bean 方法、@ComponentScan、@Import、@ImportResource 等注解,并将解析出的新 Bean 定义注册到容器中。可以说,它是 Spring 从 XML 配置转向注解配置的基石。
一、类定义与接口实现
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware
-
BeanDefinitionRegistryPostProcessor:继承自BeanFactoryPostProcessor,允许在标准BeanFactoryPostProcessor执行之前向容器注册额外的 Bean 定义。ConfigurationClassPostProcessor正是利用这个接口在容器刷新的早期阶段处理配置类。 -
PriorityOrdered:拥有最高优先级,确保它比其他后置处理器更早执行。 -
ResourceLoaderAware、BeanClassLoaderAware、EnvironmentAware:注入所需的基础设施对象。
二、核心方法:postProcessBeanDefinitionRegistry
这是 ConfigurationClassPostProcessor 的入口,在容器 refresh() 的 invokeBeanFactoryPostProcessors 阶段被调用。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 生成一个哈希值,用于检测重复执行
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException("...");
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException("...");
}
this.registriesPostProcessed.add(registryId);
// 核心处理逻辑
processConfigBeanDefinitions(registry);
}
processConfigBeanDefinitions 方法
这个方法包含了处理配置类的完整逻辑:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 获取容器中所有已注册的 Bean 名称
String[] candidateNames = registry.getBeanDefinitionNames();
// 遍历所有 BeanDefinition,找出候选的配置类
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 检查是否是 Full 或 Lite 配置类
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
// 如果已经处理过(标记了属性),则跳过,避免重复处理
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanName);
}
}
// 检查是否符合配置类的条件(带有 @Configuration 或 @Bean 方法、@Component 等)
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 如果没有找到任何配置类,直接返回
if (configCandidates.isEmpty()) {
return;
}
// 对配置类按照 @Order 注解进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 创建解析器需要的组件
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
// 检查是否已存在自定义的 BeanNameGenerator
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
// 初始化环境
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 创建配置类解析器
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 需要处理的配置类集合
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 已经解析过的配置类集合
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 1. 解析配置类(核心)
parser.parse(candidates);
parser.validate();
// 获取解析出的所有配置类(包括递归解析出的)
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// 2. 读取配置类,生成 BeanDefinition
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
// 将已解析的加入集合
alreadyParsed.addAll(configClasses);
// 清空候选列表,准备处理新注册的配置类
candidates.clear();
// 如果当前 registry 中的 BeanDefinition 数量大于之前,说明有新的配置类被注册(例如通过 @Import 引入的)
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty()); // 循环直到没有新配置类出现
// 将处理标记存入 BeanDefinition,防止重复处理
for (BeanDefinitionHolder holder : configCandidates) {
ConfigurationClassUtils.setConfigurationClassAttribute(holder.getBeanDefinition(),
parser.getConfigurationClasses().contains(holder.getBeanDefinition().getBeanClassName()));
}
// 可选:将 ImportRegistry 注册为单例 Bean,以便在运行时查询导入信息
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
流程总结:
-
收集候选配置类:遍历所有已注册的
BeanDefinition,通过ConfigurationClassUtils.checkConfigurationClassCandidate判断是否是配置类(Full 或 Lite)。 -
排序:按
@Order排序。 -
创建解析器:
ConfigurationClassParser负责解析配置类。 -
循环解析:
-
parser.parse(candidates)解析当前候选配置类(会递归处理@ComponentScan、@Import等)。 -
获取所有解析出的
ConfigurationClass。 -
使用
reader.loadBeanDefinitions(configClasses)将解析结果注册为BeanDefinition。 -
检查是否有新的配置类被注册(例如通过
@Import引入的普通类,或者@ComponentScan扫描到的配置类),如果有,则继续下一轮循环。
-
-
清理与标记:标记已处理的 BeanDefinition,避免重复处理。
-
注册 ImportRegistry:供运行时查询导入信息。
三、ConfigurationClassUtils 工具类
ConfigurationClassUtils 提供了几个静态方法用于判断和标记配置类:
-
checkConfigurationClassCandidate:判断一个BeanDefinition是否是配置类候选。-
如果类上有
@Configuration注解,则为 Full 配置类(configurationClass属性设为"full")。 -
如果类上有
@Component、@ComponentScan、@Import、@ImportResource注解,或者类中有@Bean方法,则为 Lite 配置类(configurationClass属性设为"lite")。 -
否则不是配置类。
-
-
isFullConfigurationClass/isLiteConfigurationClass:检查BeanDefinition的configurationClass属性值。 -
getOrder:获取配置类的@Order值,用于排序。
四、ConfigurationClassParser 解析器
ConfigurationClassParser 是真正解析配置类的核心。它的 parse 方法接收一组候选配置类,然后逐个调用 processConfigurationClass 进行解析。
processConfigurationClass 方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 条件评估:如果 @Conditional 条件不满足,则跳过
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 检查是否已经处理过
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
// 如果同一个类被多次导入,合并导入的 Bean 定义
existingClass.mergeImportedBy(configClass);
}
// 否则,已经存在的非导入类优先级更高,忽略导入的
return;
}
else {
// 显式注册的配置类会覆盖导入的
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// 递归解析配置类及其父类
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
// 将解析后的配置类存入 Map
this.configurationClasses.put(configClass, configClass);
}
doProcessConfigurationClass 核心递归方法
这个方法按顺序处理配置类上的各种注解,返回父类的 SourceClass 以便继续递归。
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// 1. 处理内部类(如果内部类也是配置类,递归处理)
processMemberClasses(configClass, sourceClass);
// 2. 处理 @PropertySource 注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"] because 'environment' is not configurable");
}
}
// 3. 处理 @ComponentScan 注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 执行扫描,将扫描到的 BeanDefinition 注册到容器,并返回扫描到的配置类
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 检查扫描到的类中是否还有配置类,如果有,需要继续解析
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 递归解析扫描到的配置类
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 4. 处理 @Import 注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 5. 处理 @ImportResource 注解
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 6. 处理 @Bean 方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 7. 处理接口上的默认方法(如果有 @Bean 方法)
processInterfaces(configClass, sourceClass);
// 8. 处理父类(如果有)
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// 返回父类的 SourceClass,外层循环会继续解析
return sourceClass.getSuperClass();
}
}
// 没有父类需要处理,返回 null 终止循环
return null;
}
处理顺序的重要性:例如,@ComponentScan 必须在 @Bean 之前处理,因为扫描可能发现新的配置类,而这些配置类内部可能包含 @Bean 方法。@Import 在 @Bean 之前,因为导入的类也可能是配置类。
processImports 方法
处理 @Import 注解是 Spring 扩展机制的核心。@Import 可以导入三种类型:
-
普通配置类:递归解析。
-
ImportSelector:调用selectImports方法,获取要导入的类名,然后递归处理这些类。 -
ImportBeanDefinitionRegistrar:暂存起来,等所有配置类解析完成后,由ConfigurationClassBeanDefinitionReader调用其registerBeanDefinitions方法。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// 导入的是 ImportSelector
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
// DeferredImportSelector 暂不处理,等到所有配置类解析完后统一处理
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 普通 ImportSelector,立即执行 selectImports
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 递归处理这些导入的类
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 导入的是 ImportBeanDefinitionRegistrar,暂存到配置类中
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 导入的是普通配置类,将当前导入栈信息附加后递归解析
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
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();
}
}
}
注意:DeferredImportSelector 会被延迟到所有配置类解析完成后,按照 @Order 排序后再统一处理。这解决了某些导入需要依赖其他配置类的场景(例如 Spring Boot 的自动配置)。
五、ConfigurationClassBeanDefinitionReader 读取器
当所有配置类解析完成后,reader.loadBeanDefinitions(configClasses) 负责将解析结果注册为 BeanDefinition。
loadBeanDefinitionsForConfigurationClass 方法
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 条件评估:再次检查 @Conditional
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
// 如果是被导入的,注册它自己(有可能被 ImportBeanDefinitionRegistrar 处理,但这里注册是为了让它的 @Bean 方法生效)
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 处理 @Bean 方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 处理 @ImportResource 引入的 XML
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 处理 ImportBeanDefinitionRegistrar
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
loadBeanDefinitionsForBeanMethod
这个方法将 @Bean 方法转换为 BeanDefinition,并设置相关属性(工厂方法名、工厂 Bean 名称、参数依赖等)。
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
// 创建 BeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
// 设置作用域
String scope = determineScopeName(metadata);
if (scope == null) {
scope = BeanDefinition.SCOPE_SINGLETON;
}
beanDef.setScope(scope);
// 设置工厂相关信息
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setFactoryMethodName(methodName);
// 设置参数依赖
// ... 省略参数处理细节
// 设置其他注解属性(@Lazy, @Primary, @DependsOn, @Role, @Description)
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
// 验证和注册
Autowire autowire = (BeanDefinition.AUTOWIRE_CONSTRUCTOR == beanDef.getAutowireMode() ?
Autowire.CONSTRUCTOR : Autowire.BY_NAME);
String qualifier = determineAutowireCandidate(metadata);
// ... 省略注册细节
}
registerBeanDefinitionForImportedConfigurationClass
为被导入的配置类本身注册一个 BeanDefinition。这通常是因为配置类被 @Import 引入,需要作为一个 Bean 存在(以便其内部的 @Bean 方法能被调用)。
六、Full 模式与 Lite 模式的区别
-
Full 模式:类上标注了
@Configuration(proxyBeanMethods = true)(默认)。Spring 会通过 CGLIB 为该类创建代理,拦截@Bean方法的调用,确保每次调用返回的都是同一个单例 Bean。这样可以保证在配置类内部直接调用@Bean方法时,不会创建多个实例。 -
Lite 模式:类上没有
@Configuration,或者设置了proxyBeanMethods = false。Spring 不会代理该类,@Bean方法会被当作普通工厂方法处理,调用它们会直接执行方法体,可能导致重复创建。Lite 模式的配置类包括带有@Component、@ComponentScan、@Import、@ImportResource的类,以及包含@Bean方法的普通类。
ConfigurationClassPostProcessor 会根据 checkConfigurationClassCandidate 中判断出的类型,在解析过程中决定是否要为其生成代理。具体的代理逻辑并不在 ConfigurationClassPostProcessor 中,而是在 @Configuration 类被实例化时,由 ConfigurationClassEnhancer 生成增强子类。
七、其他内部类
-
ConfigurationClass:代表一个解析后的配置类,包含其元数据、导入的资源配置、@Bean方法集合、ImportBeanDefinitionRegistrar列表等。 -
BeanMethod:封装一个@Bean方法,包含方法元数据和所属配置类。 -
DeferredImportSelectorHandler:延迟处理DeferredImportSelector,按组排序后处理。 -
ImportStack:用于检测循环导入。
八、总结
ConfigurationClassPostProcessor 是 Spring 注解驱动的基石,它的核心职责可以概括为:
-
识别配置类:通过
@Configuration、@Bean等注解识别所有配置类。 -
递归解析:处理配置类上的
@ComponentScan、@Import、@Bean等注解,生成完整的配置类模型。 -
注册 Bean 定义:将解析出的所有
@Bean方法、导入的配置类、ImportBeanDefinitionRegistrar动态注册的 Bean 定义,注册到容器中。 -
支持条件化配置:通过
@Conditional注解,在解析和注册阶段进行条件判断。 -
实现 Full 模式代理:为
@Configuration类生成 CGLIB 代理,保证单例语义。
理解 ConfigurationClassPostProcessor 的工作原理,有助于深入掌握 Spring 的内部扩展机制,以及如何利用 @Import、ImportSelector、ImportBeanDefinitionRegistrar 实现模块化配置和自动装配。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)