Spring源码阅读 - ConfigurationClassPostProcessor 配置类处理
1. 概述
当使用 spring-context 搭建简单 demo 时,调用 BeanDefinitionRegistryPostProcessor 第一个就是 ConfigurationClassPostProcessor
同时注意到它干了什么,它 getBean 立即将其转换为 Bean / 实例化了
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
2. 流程
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 一致性哈希,计算了一致性哈希或者没有重写 hashCode 但是调用了,那么这个对象的 synchronized 锁就不能再处于无锁状态了
int registryId = System.identityHashCode(registry);
// 这个是涉及多个上下文环境的那种处理吗
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
// 为什么上下分两个 ?
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 所有的 BD,注意这里包含 BeanFactoryPostProcessor , 当前类也包含,也就是说前面的 getBean 获取当前这个 BeanFactoryPostProcessor 并未将 BD 给删除
String[] candidateNames = registry.getBeanDefinitionNames();
// 由于上面是获取容器内所有 BD,因此可预见的是当前方法只会走一次,即便后续查找到其他 BD 也需要进行解析,也不会走当前方法
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 属性非空说明被解析过了
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 配置类: Configuration、ComponentScan、Bean、Import、ImportResource
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
// 为空,没有配置类需要解析,直接返回
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
// BeanName 生成器, 那么如何自定义呢
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
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();
}
// Parse each @Configuration class
// 处理 @Configuration 注解的类
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 {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 解析配置类,从上下可分析出来,即便类似 @ComponentScan 查找到其他 BD,肯定也是直接进 Parser 内部的代码进行配置类解析了
parser.parse(candidates);
parser.validate();
// 这里面应该是 Parser 记录的解析过的配置类
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 去除已经解析过的,这个实际主要是用在后面 loadBeanDefinitions 去除不必要的 BD, removeAll 是为了减少判断
// alreadyParsed 是上一次循环已经解析过的,去除上一次解析过的就是这一次解析过的,主要是 Parser 是复用的,所以需要 removeAll
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 额, 这里对类似执行了一边 shouldSkip, 将不满足条件注入的BD从容器去除
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
candidates.clear();
// 什么情况会发生注入BD进了容器,但是却没有被解析呢?
// 下面逻辑直接略, 反正是筛选出没有解析过的 BD 放到 candidates 循环过来解析
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());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
// @Configuration 配置类需要,继承 ImportAware 接口即会传入配置类 @Import 注解的元数据信息
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
3. 具体配置类解析流程
org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 一般是进入这里,其他的暂略
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 这里也判断一遍, 在容器传入启动类使用 Reader 解析时传入的 ConfigurationPhase 为 null, 默认也是作为 PARSE_CONFIGURATION 进行解析的
// 就是 Conditional 注解的解析,满足条件才会进行解析
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// configurationClasses 保存着已解析的配置类
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
// 已经被解析过了
if (existingClass != null) {
// 那这个配置类是否是通过一个配置类的 @Import 导入的 或者说是一个配置类的内部类从而导入的
// 内部类的情况是咋样?
if (configClass.isImported()) {
// 已存在的这个配置类也是被一个配置类 @Import 导入的或 ...
if (existingClass.isImported()) {
// 合并,说明相同的一个类被两个配置类均 @Import 了,那么后面会造成什么情况?若其中一个配置类的@Conditional 不满足那么它就不会被注入容器?
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
// 说明这个类即被 @Import 了, 又被通过其他方式注入了, 略, 也即被其他方式注入的优先
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
// 当前配置类不是被导入的,那么不管前一个怎样,以后面的这个为准
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
// 这个是处理父子类的问题,一般注入子类,那么后面会查看是否有父类,父类会返回为 sourceClass,再解析 sourceClass
// 但是配置类还是认为是最初的子类 configClass
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// 处理的配置类,可见这里就可能漏掉一些类,比如上面处理 configClass 可能返回其他 sourceClass, 那么这些被处理的 sourceClass 就没有被加入集合
this.configurationClasses.put(configClass, configClass);
}
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// @Configuration、@... 等都是继承 Component
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
// 处理内部类的,也是暂略
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
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() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
// REGISTER_BEAN 阶段,那么和 PARSE_CONFIGURATION 阶段的不同?
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
// 指定包扫描,扫描包及其子包,实际最后还是使用 ClassPathBeanDefinitionScanner 来进行扫描的,MyBatis 扫描接口好像也是使用它扫描的
// ClassPathBeanDefinitionScanner 扫描到后会先立即注入BD,也就是这里返回的都是已经注入的BD
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
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());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
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);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
// 若配置类作为子类,继承了一个父类,还会继续解析父类的数据
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
4. 几个点
-
配置类:@Configuration、@Component、@ComponentScan、@Import、@ImportResource
- CONFIGURATION_CLASS_FULL:@Configuration + proxyBeanMethods 为 true(默认)
- CONFIGURATION_CLASS_LITE:@Configuration + proxyBeanMethods 为 false、@Component、@ComponentScan、@Import、@ImportResource
- 这些都是在 org.springframework.context.annotation.ConfigurationClassUtils 配置和处理的
-
问题:BeanDefinition 的分类?
-
处理的几个关键的注解:@Component、@ComponentScan、@Import、@ImportResource、@Bean
-
ConfigurationClassPostProcessor
- 继承 BeanDefinitionRegistryPostProcessor 接口
- 配置类的处理,特别是 @ComponentScan 的处理
-
ConfigurationClassParser
ConfigurationClassPostProcessor
实际是使用了一个这个实例进行具体的配置类解析的