Skip to content

Latest commit

 

History

History
520 lines (419 loc) · 25.6 KB

File metadata and controls

520 lines (419 loc) · 25.6 KB

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) {
	}
}

四、主要功能

  1. 处理合并后的 Bean 定义

    • 在 Spring 中,一个 bean 可以继承另一个 bean 的配置,产生所谓的 "合并后的" bean 定义。这个合并的定义包括原始 bean 定义和任何父 bean 定义中的属性。MergedBeanDefinitionPostProcessor 允许我们在 bean 的实例化和初始化之前,基于这个合并的定义执行定制逻辑。
  2. 缓存元数据

    • 这个接口常常被用于检查 bean 定义并缓存相关的元数据,从而加速后续的 bean 实例化和初始化。例如,Spring 的 AutowiredAnnotationBeanPostProcessor 使用它来缓存 @Autowired@Value 注解的信息。
  3. 修改合并后的 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,是为了确保MyBeanMergedBeanDefinitionPostProcessor 被 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:初始化完成
Loading

七、源码分析

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方法中,遍历每一个 MergedBeanDefinitionPostProcessorpostProcessMergedBeanDefinition 方法,提供了一个自定义或查询合并的 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);
        }
    }
}

八、注意事项

  1. 调用时机

    • postProcessMergedBeanDefinition 是在 bean 处于一个 "半实例化" 的状态。更确切地说,在此时,bean 的实例已经被创建,但属性注入、初始化方法等还没有执行,这意味着我们不应该在此方法中尝试访问 bean 实例。
  2. 避免修改不可变属性

    • 虽然我们可以在 postProcessMergedBeanDefinition 方法中修改 RootBeanDefinition,但应该小心只修改那些预期为可变的属性。例如(Bean的类名,构造函数参数值,懒加载标记,依赖信息,作用域,等等)
  3. 影响性能

    • 如果 postProcessMergedBeanDefinition 执行的操作很重,这可能会影响应用的启动性能,因为它会被每个 bean 的创建过程调用。
  4. 防止无限递归

    • 如果我们在 postProcessMergedBeanDefinition 方法中尝试获取其他 beans,这可能会触发那些 beans 的创建,从而再次调用 postProcessMergedBeanDefinition。我们应该注意避免这种无限递归的情况。

九、总结

最佳实践总结

  1. 启动类入口

    • MergedBeanDefinitionPostProcessorApplication 是应用的主入口。在这里,我们使用了 AnnotationConfigApplicationContext 来初始化和配置 Spring 容器,并为其提供了一个配置类 MyConfiguration
  2. 配置类

    • MyConfiguration 中,我们定义了两个bean:一个是自定义的 MyMergedBeanDefinitionPostProcessor,另一个是一个简单的 MyBean 类实例。
  3. 自定义后处理器

    • MyMergedBeanDefinitionPostProcessor 实现了 MergedBeanDefinitionPostProcessor 接口,允许我们在bean的实例化之前处理和修改其定义。在这个示例中,我们检查bean的字段是否有 MyValue 注解。如果有,我们将字段的名称和注解的值存储在一个映射中。然后,在bean的实例化和初始化后,我们检查是否需要为字段设置值。如果字段的当前值是 null,我们使用 MyValue 注解提供的值来设置它。
  4. 自定义注解

    • MyValue 是一个简单的自定义注解,用于指定一个字段的默认值。
  5. 目标Bean

    • MyBean 是一个简单的Java类,其中一个字段 message 被标记为 MyValue("hello world")。这意味着,如果在Spring容器初始化此bean时,message 字段没有被明确设置一个值,那么它将使用 MyValue 注解中的默认值 "hello world"。
  6. 执行结果

    • 当应用程序运行时,Spring容器会实例化并初始化 MyBean。由于 message 字段的值未被明确设置,因此 MyMergedBeanDefinitionPostProcessor 将使用 MyValue 注解中的默认值 "hello world" 为其赋值。最后,应用程序输出 "message = hello world"。

源码分析总结

  1. 启动类

    • 应用的主入口是MergedBeanDefinitionPostProcessorApplication。它使用AnnotationConfigApplicationContext来初始化Spring容器,并传入配置类MyConfiguration
  2. 初始化Spring容器

    • AnnotationConfigApplicationContext的构造函数中,除了一些基本的配置外,它主要调用了refresh()方法来完成容器的初始化。
  3. 容器刷新

    • refresh()方法是在AbstractApplicationContext中定义的,用于完成容器的初始化。其中,finishBeanFactoryInitialization(beanFactory)方法被用来实例化所有非懒加载的单例Bean对象。
  4. 实例化单例Beans

    • preInstantiateSingletons()方法在DefaultListableBeanFactory中被调用,用于预先实例化所有非懒加载的单例bean。该方法通过调用getBean(beanName)来实例化和初始化bean。
  5. Bean获取

    • getBean()方法在AbstractBeanFactory中定义,它最终会调用doGetBean()方法来完成实际的Bean创建工作。
  6. Bean的创建

    • doGetBean()方法处理bean的查找、创建和依赖处理。如果请求的bean是一个单例并且尚未创建,则它将使用getSingleton()方法从单例缓存中获取或创建新的实例。
  7. 处理单例Beans

    • DefaultSingletonBeanRegistry中,getSingleton()方法用于从单例缓存中获取已存在的bean或使用ObjectFactory创建新的实例。
  8. 实际Bean的创建

    • AbstractAutowireCapableBeanFactory中,createBean()方法是Bean创建的入口,它主要调用doCreateBean()方法。在doCreateBean()中,applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)方法允许MergedBeanDefinitionPostProcessors修改合并的bean定义。
  9. 应用合并的Bean定义后处理器

    • applyMergedBeanDefinitionPostProcessors()方法遍历并调用每个MergedBeanDefinitionPostProcessorpostProcessMergedBeanDefinition()方法。这为每个合并的Bean定义提供了自定义或查询的机会。
  10. 自定义后处理器逻辑

    • 在我们的例子中,MyMergedBeanDefinitionPostProcessor对带有MyValue注解的属性进行了处理。它在postProcessMergedBeanDefinition()中检查每个字段是否有MyValue注解,并为这些字段收集默认值。