Spring 源码阅读 38:postProcessBeanFactory 对 @Configuration 配置的解析和处理(1)

本文涉及的产品
云解析 DNS,旗舰版 1个月
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
简介: 本文分析了 Spring 在后处理器中,对配置类进行增强处理的前半部分流程,也就是如何从容器中找到需要进行增强处理的配置类的 BeanDefinition。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 37:postProcessBeanDefinitionRegistry 对 @Configuration 配置的解析和处理(2)

概述

之前的两篇文章介绍了 ConfigurationClassPostProcessor 后处理器的postProcessBeanDefinitionRegistry方法对配置类的处理,在处理完成之后,后处理器的postProcessBeanFactory方法还会被执行。本文开始分析postProcessBeanFactory方法对配置类执行了哪些工作。

处理过程

首先进入postProcessBeanFactory方法查看源码。

// org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanFactory@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory) {
intfactoryId=System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
thrownewIllegalStateException(
"postProcessBeanFactory already called on this post-processor against "+beanFactory);
   }
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...// Simply call processConfigurationClasses lazily at this point then.processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
   }
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(newImportAwareBeanPostProcessor(beanFactory));
}

首先获取了当前 BeanFactory 容器的factoryId,然后判断postProcessBeanFactory方法是否已经执行过了,如果执行过了,则排出异常,确保这个方法不被重复执行。然后又判断了postProcessBeanDefinitionRegistry是不是被执行过了,如果没有被执行,则调用processConfigBeanDefinitions方法,这里是为了确保当前处理方法的业务流程在执行之前,processConfigBeanDefinitions方法已经被先执行过了。

以上的判断都执行完成之后,是主要流程的执行,其中的重点就是enhanceConfigurationClasses方法。

enhanceConfigurationClasses 方法

从方法的名称来看,这个方法是对配置类的增强。进入enhanceConfigurationClasses方法的源码。

image.png

我们先大概浏览一下这个方法的代码结构,大概可以分为两个大部分。

首先,创建了一个 Map 集合configBeanDefs,Key 是字符串类型,Value 是 AbstractBeanDefinition 类型,可以看出,这里面存放的是配置类的 BeanDefinition。下面紧跟的for循环中,会遍历容器中所有的 BeanDefinition,进行筛选,将符合条件的 BeanDefinition 放到configBeanDefs中,如果遍历完之后configBeanDefs还是空的,则方法直接返回。

然后,会通过下一个for循环对configBeanDefs中所有的 Value 进行遍历,对配置类进行增强。

下面我们就分为两部分进行分析。

配置类的筛选

首先是第一部分,筛选配置类的部分。

for (StringbeanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinitionbeanDef=beanFactory.getBeanDefinition(beanName);
ObjectconfigClassAttr=beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadatamethodMetadata=null;
if (beanDefinstanceofAnnotatedBeanDefinition) {
methodMetadata= ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
   }
if ((configClassAttr!=null||methodMetadata!=null) &&beanDefinstanceofAbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method// -> resolve bean class at this point...AbstractBeanDefinitionabd= (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
         }
catch (Throwableex) {
thrownewIllegalStateException(
"Cannot load configuration class: "+beanDef.getBeanClassName(), ex);
         }
      }
   }
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDefinstanceofAbstractBeanDefinition)) {
thrownewBeanDefinitionStoreException("Cannot enhance @Configuration bean definition '"+beanName+"' since it is not stored in an AbstractBeanDefinition subclass");
      }
elseif (logger.isInfoEnabled() &&beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '"+beanName+"' since its singleton instance has been created too early. The typical cause "+"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor "+"return type: Consider declaring such methods as 'static'.");
      }
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
   }
}

对当前遍历到的每一个 BeanDefinition,除了获取到 BeanDefinition 对象beanDef之外,还会获取configClassAttr

configClassAttr是 BeanDefinition 对象中的一个属性值,属性名为org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass。这个属性是在执行后处理器的postProcessBeanDefinitionRegistry方i 可开开心心学习法时设置的,默认情况下是full。这个值其实就代表了@Configuration注解的proxyBeanMethods属性为默认值true

除此之外, 还声明了一个 MethodMetadata 类型的变量methodMetadata,值暂时为空。

接着,是三个if语句块,先看第一个。

if (beanDefinstanceofAnnotatedBeanDefinition) {
methodMetadata= ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}

这里判断了当前的 BeanDefinition 的类型是不是 AnnotatedBeanDefinition。如果是的话,则获取它的factoryMethodMetadata属性,作为methodMetadata的值。factoryMethodMetadata是它的工厂方法相关的信息,一般情况下是一个空值。

再看第二个if语句块。

if ((configClassAttr!=null||methodMetadata!=null) &&beanDefinstanceofAbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method// -> resolve bean class at this point...AbstractBeanDefinitionabd= (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
      }
catch (Throwableex) {
thrownewIllegalStateException(
"Cannot load configuration class: "+beanDef.getBeanClassName(), ex);
      }
   }
}

根据前面的条件可以知道,这里的判断条件是成立的。在if语句块中,会判断 BeanDefinition 对应的类型是否存在,如果不存在,则通过类加载器加载。这一部比较简单。

下面看第三个if语句块。

if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDefinstanceofAbstractBeanDefinition)) {
thrownewBeanDefinitionStoreException("Cannot enhance @Configuration bean definition '"+beanName+"' since it is not stored in an AbstractBeanDefinition subclass");
   }
elseif (logger.isInfoEnabled() &&beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '"+beanName+"' since its singleton instance has been created too early. The typical cause "+"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor "+"return type: Consider declaring such methods as 'static'.");
   }
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}

这里会判断configClassAttr的属性值是不是full,这个属性值前文已经介绍过了。如果是的话,则将它添加到configBeanDefs中,这里的 Key 是 Bean 的名称,值是被转换为 AbstractBeanDefinition 类型的当前的 BeanDefinition。

最后,当for循环执行完之后,configBeanDefs集合仍然为空,说明没有配置类需要进行增强处理,方法直接返回。代码如下:

if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediatelyreturn;
}

反之,如果configBeanDefs中是有元素的,那么则开始后续的步骤,也就是对这些配置类进行增强处理。

总结

本文分析了postProcessBeanFactory处理方法调用的enhanceConfigurationClasses方法对配置类进行增强处理的前半部分流程,也就是如何从容器中找到需要进行增强处理的配置类的 BeanDefinition。下一篇讲分析后续的流程,也就是如何对这些配置类进行增强处理。


目录
相关文章
|
7天前
|
Java 开发者 Spring
解析Spring中Bean的生命周期
解析Spring中Bean的生命周期
15 2
|
7天前
|
XML Java 数据格式
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
17 3
|
6天前
|
存储 Java 程序员
Spring 注册BeanPostProcessor 源码阅读
Spring 注册BeanPostProcessor 源码阅读
|
2天前
|
运维 Java 测试技术
Spring运维之boo项目表现层测试加载测试的专用配置属性以及在JUnit中启动web服务器发送虚拟请求
Spring运维之boo项目表现层测试加载测试的专用配置属性以及在JUnit中启动web服务器发送虚拟请求
9 3
|
6天前
|
消息中间件 Java Kafka
集成Kafka到Spring Boot项目中的步骤和配置
集成Kafka到Spring Boot项目中的步骤和配置
33 7
|
6天前
|
druid Java 关系型数据库
在Spring Boot中集成Druid实现多数据源有两种常用的方式:使用Spring Boot的自动配置和手动配置。
在Spring Boot中集成Druid实现多数据源有两种常用的方式:使用Spring Boot的自动配置和手动配置。
58 5
|
7天前
|
Java 应用服务中间件 Spring
解析Spring Boot自动装配的原理与机制
解析Spring Boot自动装配的原理与机制
17 4
|
5天前
|
监控 Java API
【Spring Boot】深入解密Spring Boot日志:最佳实践与策略解析
【Spring Boot】深入解密Spring Boot日志:最佳实践与策略解析
15 1
|
6天前
|
Java Spring 容器
在 Spring Boot 中,条件装配(Conditional Configuration)和条件注解(Conditional Annotations)
在 Spring Boot 中,条件装配(Conditional Configuration)和条件注解(Conditional Annotations)
10 1
|
2天前
|
Java Spring
spring基于注解配置数据源
spring基于注解配置数据源
8 0

推荐镜像

更多