26 KiB
MergedBeanDefinitionPostProcessor
一、基本信息
✒️ 作者 - Lex 📝 博客 - 我的CSDN 📚 文章目录 - 所有文章 🔗 源码地址 - MergedBeanDefinitionPostProcessor源码
二、接口描述
MergedBeanDefinitionPostProcessor
是 Spring 框架中的一个接口,主要用于在 bean 定义被合并后(但在 bean 实例化之前)进行后处理。它扩展了 BeanPostProcessor
,增加了处理合并 bean 定义的能力。
三、接口源码
MergedBeanDefinitionPostProcessor
是 Spring 框架自 2.5 版本开始引入的一个核心接口。其中的核心方法是postProcessMergedBeanDefinition
主要用途为提供了一个自定义或查询合并的 bean 定义的机会,例如应用自定义注释、修改 bean 元数据或基于合并的 bean 定义实现自定义行为。
/**
* 用于在运行时后处理合并的 bean 定义的回调接口。
* BeanPostProcessor 实现可以实现此子接口,以便在 Spring 的 BeanFactory
* 用于创建 bean 实例的时候对已合并的 bean 定义(原始 bean 定义的处理副本)进行后处理。
*
* #postProcessMergedBeanDefinition 方法可以用于内省 bean 定义,
* 例如在后缀处理器 bean 的实例之前准备一些缓存的元数据。它也允许修改 bean 定义,
* 但仅限于那些实际上用于并发修改的定义属性。本质上,这只应用于 RootBeanDefinition
* 本身上定义的操作,但不包括其基类的属性。
*
* @author Juergen Hoeller
* @since 2.5
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getMergedBeanDefinition
*/
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
/**
* 后处理指定 bean 的给定合并 bean 定义。
* @param beanDefinition bean 的合并定义
* @param beanType 管理的 bean 实例的实际类型
* @param beanName bean 的名称
* @see AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
*/
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
/**
* 通知指定名称的 bean 定义已被重置,
* 这个 post-processor 应该清除受影响的 bean 的任何元数据。
* 默认实现是空的。
* @param beanName bean 的名称
* @since 5.1
* @see DefaultListableBeanFactory#resetBeanDefinition
*/
default void resetBeanDefinition(String beanName) {
}
}
四、主要功能
-
处理合并后的 Bean 定义
- 在 Spring 中,一个 bean 可以继承另一个 bean 的配置,产生所谓的 "合并后的" bean 定义。这个合并的定义包括原始 bean 定义和任何父 bean 定义中的属性。
MergedBeanDefinitionPostProcessor
允许我们在 bean 的实例化和初始化之前,基于这个合并的定义执行定制逻辑。
- 在 Spring 中,一个 bean 可以继承另一个 bean 的配置,产生所谓的 "合并后的" bean 定义。这个合并的定义包括原始 bean 定义和任何父 bean 定义中的属性。
-
缓存元数据
- 这个接口常常被用于检查 bean 定义并缓存相关的元数据,从而加速后续的 bean 实例化和初始化。例如,Spring 的
AutowiredAnnotationBeanPostProcessor
使用它来缓存@Autowired
和@Value
注解的信息。
- 这个接口常常被用于检查 bean 定义并缓存相关的元数据,从而加速后续的 bean 实例化和初始化。例如,Spring 的
-
修改合并后的 Bean 定义
- 虽然不是主要的使用场景,但
MergedBeanDefinitionPostProcessor
也允许修改合并后的 bean 定义。但这种修改应该小心进行,并且通常只限于那些真正用于并发修改的定义属性。
- 虽然不是主要的使用场景,但
五、最佳实践
首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext
(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个MyConfiguration
组件类。然后从Spring上下文中获取一个MyBean
类型的bean,最后打印了该getMessage
方法返回的值。
public class MergedBeanDefinitionPostProcessorApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyBean bean = context.getBean(MyBean.class);
System.out.println("message = " + bean.getMessage());
}
}
这里使用@Bean
注解,定义了两个Bean,是为了确保MyBean
, MergedBeanDefinitionPostProcessor
被 Spring 容器执行
@Configuration
public class MyConfiguration {
@Bean
public static MergedBeanDefinitionPostProcessor myBeanPostProcessor() {
return new MyMergedBeanDefinitionPostProcessor();
}
@Bean
public MyBean myBean() {
return new MyBean();
}
}
实现了 MergedBeanDefinitionPostProcessor
的类 MyMergedBeanDefinitionPostProcessor
。这个类针对带有自定义注解 MyValue
的属性进行处理。在postProcessMergedBeanDefinition
方法中,对于每个字段,检查是否有 MyValue
注解。如果有,则获取注解的值,并将字段和该值存储在 defaultValues
映射中。在postProcessAfterInitialization
方法中,检查 metadata
是否包含这个 bean 的名称。如果包含,表示这个 bean 有需要处理的字段,还需检查该字段的当前值。如果字段的值为 null,则使用注解提供的值来设置该字段的值。
public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {
/**
* 记录元数据
*/
private Map<String, Map<Field, String>> metadata = new HashMap<>();
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
Field[] fields = beanType.getDeclaredFields();
Map<Field, String> defaultValues = new HashMap<>();
for (Field field : fields) {
if (field.isAnnotationPresent(MyValue.class)) {
MyValue annotation = field.getAnnotation(MyValue.class);
defaultValues.put(field, annotation.value());
}
}
if (!defaultValues.isEmpty()) {
metadata.put(beanName, defaultValues);
}
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (metadata.containsKey(beanName)) {
Map<Field, String> defaultValues = metadata.get(beanName);
for (Map.Entry<Field, String> entry : defaultValues.entrySet()) {
Field field = entry.getKey();
String value = entry.getValue();
try {
field.setAccessible(true);
if (field.get(bean) == null) {
field.set(bean, value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return bean;
}
}
一个简单的注解类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyValue {
String value();
}
一个简单的Bean类
public class MyBean {
@MyValue("hello world")
private String message;
public String getMessage() {
return message;
}
}
运行结果发现,当 MyBean
实例化并初始化时,由于 message
字段的值未被明确设置,MyMergedBeanDefinitionPostProcessor
会使用 MyValue
注解中的默认值,即 "hello world",来为其赋值。
message = hello world
六、时序图
sequenceDiagram
Title: MergedBeanDefinitionPostProcessor时序图
participant MergedBeanDefinitionPostProcessorApplication
participant AnnotationConfigApplicationContext
participant AbstractApplicationContext
participant DefaultListableBeanFactory
participant AbstractBeanFactory
participant DefaultSingletonBeanRegistry
participant AbstractAutowireCapableBeanFactory
participant MyMergedBeanDefinitionPostProcessor
MergedBeanDefinitionPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)<br>创建上下文
AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()<br>刷新上下文
AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)<br>初始化Bean工厂
AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()<br>实例化单例
DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)<br>获取Bean
AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)<br>执行获取Bean
AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)<br>获取单例Bean
DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()<br>获取Bean实例
AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)<br>创建Bean
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)<br>执行Bean创建
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)<br>应用Post处理器
AbstractAutowireCapableBeanFactory->>MyMergedBeanDefinitionPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)<br>处理合并定义
AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象
AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象
AnnotationConfigApplicationContext-->>MergedBeanDefinitionPostProcessorApplication:初始化完成
七、源码分析
public class MergedBeanDefinitionPostProcessorApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyBean bean = context.getBean(MyBean.class);
System.out.println("message = " + bean.getMessage());
}
}
在org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext
构造函数中,执行了三个步骤,我们重点关注refresh()
方法
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
org.springframework.context.support.AbstractApplicationContext#refresh
方法中,我们重点关注一下finishBeanFactoryInitialization(beanFactory)
这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。
@Override
public void refresh() throws BeansException, IllegalStateException {
// ... [代码部分省略以简化]
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// ... [代码部分省略以简化]
}
在org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
方法中,会继续调用DefaultListableBeanFactory
类中的preInstantiateSingletons
方法来完成所有剩余非懒加载的单列Bean对象。
/**
* 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。
*
* @param beanFactory 要初始化的bean工厂
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// ... [代码部分省略以简化]
// 完成所有剩余非懒加载的单列Bean对象。
beanFactory.preInstantiateSingletons();
}
在org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中getBean(beanName)
是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用getBean
方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。
public void preInstantiateSingletons() throws BeansException {
// ... [代码部分省略以简化]
// 循环遍历所有bean的名称
for (String beanName : beanNames) {
getBean(beanName);
}
// ... [代码部分省略以简化]
}
在org.springframework.beans.factory.support.AbstractBeanFactory#getBean()
方法中,又调用了doGetBean
方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,doGetBean
负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
在org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// ... [代码部分省略以简化]
// 开始创建bean实例
if (mbd.isSingleton()) {
// 如果bean是单例的,我们会尝试从单例缓存中获取它
// 如果不存在,则使用lambda创建一个新的实例
sharedInstance = getSingleton(beanName, () -> {
try {
// 尝试创建bean实例
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// ... [代码部分省略以简化]
}
});
// 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// ... [代码部分省略以简化]
// 确保返回的bean实例与请求的类型匹配
return adaptBeanInstance(name, beanInstance, requiredType);
}
在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()
方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的ObjectFactory
创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// 断言bean名称不能为空
Assert.notNull(beanName, "Bean name must not be null");
// 同步访问单例对象缓存,确保线程安全
synchronized (this.singletonObjects) {
// 从缓存中获取单例对象
Object singletonObject = this.singletonObjects.get(beanName);
// 如果缓存中没有找到
if (singletonObject == null) {
// ... [代码部分省略以简化]
try {
// 使用工厂创建新的单例实例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// ... [代码部分省略以简化]
}
catch (BeanCreationException ex) {
// ... [代码部分省略以简化]
}
finally {
// ... [代码部分省略以简化]
}
// ... [代码部分省略以简化]
}
// 返回单例对象
return singletonObject;
}
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()
方法中,主要的逻辑是调用 doCreateBean
,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ... [代码部分省略以简化]
try {
// 正常的bean实例化、属性注入和初始化。
// 这里是真正进行bean创建的部分。
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
// 记录bean成功创建的日志
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// ... [代码部分省略以简化]
}
catch (Throwable ex) {
// ... [代码部分省略以简化]
}
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
方法中,在 bean 的实例化前,会对合并的 bean 定义进行处理。这是 MergedBeanDefinitionPostProcessors
可以介入和修改合并后的 bean 定义的地方,为了确保对每个 bean 定义只执行一次后处理,有一个 postProcessed
标志,如果此标志为 false
,则会调用 applyMergedBeanDefinitionPostProcessors
方法应用所有的 MergedBeanDefinitionPostProcessors
,为了线程安全,此操作在 mbd.postProcessingLock
的同步块中执行。这确保了并发的 bean 创建请求不会导致对同一 bean 定义的重复后处理。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ... [代码部分省略以简化]
// 允许MergedBeanDefinitionPostProcessor修改合并的bean定义
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
// ... [代码部分省略以简化]
}
mbd.postProcessed = true;
}
}
// ... [代码部分省略以简化]
// 返回创建和初始化后的bean
return exposedObject;
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
方法中,遍历每一个 MergedBeanDefinitionPostProcessor
的 postProcessMergedBeanDefinition
方法,提供了一个自定义或查询合并的 bean 定义的机会。
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
最后执行到我们自定义的逻辑中,对于每个字段,检查是否带有 MyValue
注解。如果带有,从该字段获取 MyValue
注解,并将字段与注解的值存储在 defaultValues
Map
中,如果 defaultValues
不为空(即存在至少一个带有 MyValue
注解的字段),则将该 Map
存储在 metadata
中,键为 bean 的名称。
public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {
/**
* 记录元数据
*/
private Map<String, Map<Field, String>> metadata = new HashMap<>();
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
Field[] fields = beanType.getDeclaredFields();
Map<Field, String> defaultValues = new HashMap<>();
for (Field field : fields) {
if (field.isAnnotationPresent(MyValue.class)) {
MyValue annotation = field.getAnnotation(MyValue.class);
defaultValues.put(field, annotation.value());
}
}
if (!defaultValues.isEmpty()) {
metadata.put(beanName, defaultValues);
}
}
}
八、注意事项
-
调用时机
postProcessMergedBeanDefinition
是在 bean 处于一个 "半实例化" 的状态。更确切地说,在此时,bean 的实例已经被创建,但属性注入、初始化方法等还没有执行,这意味着我们不应该在此方法中尝试访问 bean 实例。
-
避免修改不可变属性
- 虽然我们可以在
postProcessMergedBeanDefinition
方法中修改RootBeanDefinition
,但应该小心只修改那些预期为可变的属性。例如(Bean的类名,构造函数参数值,懒加载标记,依赖信息,作用域,等等)
- 虽然我们可以在
-
影响性能
- 如果
postProcessMergedBeanDefinition
执行的操作很重,这可能会影响应用的启动性能,因为它会被每个 bean 的创建过程调用。
- 如果
-
防止无限递归
- 如果我们在
postProcessMergedBeanDefinition
方法中尝试获取其他 beans,这可能会触发那些 beans 的创建,从而再次调用postProcessMergedBeanDefinition
。我们应该注意避免这种无限递归的情况。
- 如果我们在
九、总结
最佳实践总结
-
启动类入口
MergedBeanDefinitionPostProcessorApplication
是应用的主入口。在这里,我们使用了AnnotationConfigApplicationContext
来初始化和配置 Spring 容器,并为其提供了一个配置类MyConfiguration
。
-
配置类
- 在
MyConfiguration
中,我们定义了两个bean:一个是自定义的MyMergedBeanDefinitionPostProcessor
,另一个是一个简单的MyBean
类实例。
- 在
-
自定义后处理器
MyMergedBeanDefinitionPostProcessor
实现了MergedBeanDefinitionPostProcessor
接口,允许我们在bean的实例化之前处理和修改其定义。在这个示例中,我们检查bean的字段是否有MyValue
注解。如果有,我们将字段的名称和注解的值存储在一个映射中。然后,在bean的实例化和初始化后,我们检查是否需要为字段设置值。如果字段的当前值是null
,我们使用MyValue
注解提供的值来设置它。
-
自定义注解
MyValue
是一个简单的自定义注解,用于指定一个字段的默认值。
-
目标Bean
MyBean
是一个简单的Java类,其中一个字段message
被标记为MyValue("hello world")
。这意味着,如果在Spring容器初始化此bean时,message
字段没有被明确设置一个值,那么它将使用MyValue
注解中的默认值 "hello world"。
-
执行结果
- 当应用程序运行时,Spring容器会实例化并初始化
MyBean
。由于message
字段的值未被明确设置,因此MyMergedBeanDefinitionPostProcessor
将使用MyValue
注解中的默认值 "hello world" 为其赋值。最后,应用程序输出 "message = hello world"。
- 当应用程序运行时,Spring容器会实例化并初始化
源码分析总结
-
启动类
- 应用的主入口是
MergedBeanDefinitionPostProcessorApplication
。它使用AnnotationConfigApplicationContext
来初始化Spring容器,并传入配置类MyConfiguration
。
- 应用的主入口是
-
初始化Spring容器
- 在
AnnotationConfigApplicationContext
的构造函数中,除了一些基本的配置外,它主要调用了refresh()
方法来完成容器的初始化。
- 在
-
容器刷新
refresh()
方法是在AbstractApplicationContext
中定义的,用于完成容器的初始化。其中,finishBeanFactoryInitialization(beanFactory)
方法被用来实例化所有非懒加载的单例Bean对象。
-
实例化单例Beans
preInstantiateSingletons()
方法在DefaultListableBeanFactory
中被调用,用于预先实例化所有非懒加载的单例bean。该方法通过调用getBean(beanName)
来实例化和初始化bean。
-
Bean获取
getBean()
方法在AbstractBeanFactory
中定义,它最终会调用doGetBean()
方法来完成实际的Bean创建工作。
-
Bean的创建
doGetBean()
方法处理bean的查找、创建和依赖处理。如果请求的bean是一个单例并且尚未创建,则它将使用getSingleton()
方法从单例缓存中获取或创建新的实例。
-
处理单例Beans
- 在
DefaultSingletonBeanRegistry
中,getSingleton()
方法用于从单例缓存中获取已存在的bean或使用ObjectFactory
创建新的实例。
- 在
-
实际Bean的创建
- 在
AbstractAutowireCapableBeanFactory
中,createBean()
方法是Bean创建的入口,它主要调用doCreateBean()
方法。在doCreateBean()
中,applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)
方法允许MergedBeanDefinitionPostProcessors
修改合并的bean定义。
- 在
-
应用合并的Bean定义后处理器
applyMergedBeanDefinitionPostProcessors()
方法遍历并调用每个MergedBeanDefinitionPostProcessor
的postProcessMergedBeanDefinition()
方法。这为每个合并的Bean定义提供了自定义或查询的机会。
-
自定义后处理器逻辑
- 在我们的例子中,
MyMergedBeanDefinitionPostProcessor
对带有MyValue
注解的属性进行了处理。它在postProcessMergedBeanDefinition()
中检查每个字段是否有MyValue
注解,并为这些字段收集默认值。
- 在我们的例子中,