Spring高手之路13——BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor解析

1. BeanFactoryPostProcessor 概览

1.1 解读 BeanFactoryPostProcessor

BeanFactoryPostProcessor位于org.springframework.beans.factory.config包中。它与BeanPostProcessor有相似的核心逻辑,但它们之间的主要区别在于它们所操作的对象。BeanFactoryPostProcessor的主要目的是对Bean的配置元数据进行操作,这意味着它可以影响Bean的初始配置数据。

Spring IoC容器实例化beans之前,特别是除了BeanFactoryPostProcessor之外的其他beansBeanFactoryPostProcessor有权利修改这些beans的配置。在Spring中,所有的beans在被完全实例化之前都是以BeanDefinition的形式存在的。BeanFactoryPostProcessor为我们提供了一个机会,使我们能够在bean完全实例化之前调整和修改这些BeanDefinition。对BeanDefinition的任何修改都会影响后续的bean实例化和初始化过程。

1.2. 如何使用 BeanFactoryPostProcessor

来看看BeanFactoryPostProcessor能如何影响BeanDefinition

假设我们需要为一系列的Tint对象赋值名字,这个名字就是bean的名字,而且要在bean实例化之前完成。

  1. 定义bean

我们定义一个简单的Tint抽象类以及其两个子类BlueYellow:

java 复制代码
package com.example.demo.bean;

public abstract class Tint {
    protected String label;
    
    public String getLabel() {
        return label;
    }
    
    public void setLabel(String label) {
        this.label = label;
    }
}
java 复制代码
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Blue extends Tint {
    @Override
    public String toString() {
        return "Blue{" + "label='" + label + '\'' + "}";
    }
}
java 复制代码
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Yellow extends Tint {
    @Override
    public String toString() {
        return "Yellow{" + "label='" + label + '\'' + "}";
    }
}
  1. 创建后置处理器

思路是在后置处理器中,我们可以获取到BeanFactory,然后操作其中的BeanDefinition

java 复制代码
package com.example.demo.processor;

import com.example.demo.bean.Tint;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

/**
 * BeanFactory后置处理器,用于设置Tint子类bean的label属性。
 * label属性的值会设置为"postProcessBeanFactory_" + beanName。
 */
@Component
public class TintLabelSetterFactoryPostProcessor implements BeanFactoryPostProcessor {

    /**
     * 在所有BeanDefinition加载完成之后,但bean实例化之前,设置label属性。
     *
     * @param beanFactory 可配置的bean工厂,可以操作BeanDefinition。
     * @throws BeansException 处理过程中的异常。
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 遍历所有bean的名字
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);

            // 检查bean的类名是否非空,且其父类是Tint
            if (beanDefinition.getBeanClassName() != null &&
                    ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), this.getClass().getClassLoader())
                            .getSuperclass().equals(Tint.class)) {

                // 添加或更新(如果属性已存在)label属性的值
                beanDefinition.getPropertyValues().add("label", "postProcessBeanFactory_" + beanName);
            }
        }
    }
}
  1. 运行测试

启动Spring容器,查看结果:

java 复制代码
package com.example.demo;

import com.example.demo.bean.Blue;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class DemoApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("com.example.demo");
        Blue blue = ctx.getBean(Blue.class);
        System.out.println(blue);
    }

}

运行之后,控制台打印Blue对象的label属性,显示后置处理器成功修改了bean的属性。

  1. 替代方法

我们也可以使用BeanPostProcessor达到BeanFactoryPostProcessor相似的效果:

java 复制代码
package com.example.demo.processor;

import com.example.demo.bean.Tint;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * TintLabelSetterPostProcessor类是一个BeanPostProcessor的实现,
 * 它为类型为Tint的bean设置'label'属性。该属性的值将被设置为"postProcessAfterInitialization_"加上bean的名称。
 * 这里是一个postProcessAfterInitialization方法,它会在bean初始化后,但在返回给调用者之前执行。
 */
@Component
public class TintLabelSetterPostProcessor implements BeanPostProcessor {

    /**
     * 对bean进行后初始化处理。如果bean是Tint类型,它的'label'属性将被设置。
     *
     * @param bean 将要处理的bean对象。
     * @param beanName bean的名称。
     * @return 可能已经修改过的bean。
     * @throws BeansException 如果在处理过程中出现错误。
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Tint) {
            Tint tint = (Tint) bean;
            tint.setLabel("postProcessAfterInitialization_" + beanName);
        }
        return bean;
    }
}

运行结果:

  1. BeanPostProcessor 与 BeanFactoryPostProcessor 的对比
特点 BeanPostProcessor BeanFactoryPostProcessor
处理目标 主要关注已经实例化的 bean 对象。 针对 BeanDefinition,也就是 bean 的元数据或者说是配置信息。
执行时机 两个关键阶段: 1. postProcessBeforeInitialization :在 Spring 完成 bean 的实例化、进行属性注入后,但在 bean 的自定义初始化(如通过@PostConstruct注解定义的方法或init-method方法)之前调用。 2. postProcessAfterInitialization:在 bean 的自定义初始化方法执行后调用 在 BeanDefinition 已经完成加载、注册到 BeanDefinitionRegistry后,但在 bean 实例化之前。这一时期主要处理 bean 的元数据。
可操作的空间 可以操作实例化的 bean 对象,如修改属性或包裹生成代理对象。 主要用于修改或添加 BeanDefinition 的属性、移除某些 BeanDefinition 等。

2. BeanDefinitionRegistryPostProcessor 深入探究

2.1 解读 BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessorSpring 容器的一个扩展点,主要用于在 Spring 容器完成对 Bean 的定义信息的加载后、但在它们真正实例化之前,进行额外的操作。

为了更好地理解,让我们用一个图书馆的类比:

想象一个新的图书馆正在组织其图书收藏。这个过程可以分为几个步骤:

  1. 制定书单:图书馆先列出了所有想要的书的名称和作者,但还没有实际购买书籍。在Spring中,这就类似于创建BeanDefinition

  2. 在这个步骤后,但在图书馆真正购买书籍之前,假设图书馆收到了一个特别的捐赠列表(BeanDefinitionRegistryPostProcessor)。这个捐赠列表允许图书馆在正式购买书籍之前添加或修改书单。在Spring中,这是使用BeanDefinitionRegistryPostProcessor在实际的bean实例化之前修改bean定义的时机。

  3. 按书单采购:此时,图书馆会按照更新后的书单进行购书。这个过程在Spring中类似于bean的实例化和属性填充。

更佳专业化的描述如下

BeanDefinitionRegistryPostProcessorSpring中的一个高级扩展接口,继承自 BeanFactoryPostProcessor。它提供了更为深入的方式来干预bean定义的注册过程。

这个接口定义于 org.springframework.beans.factory.support 包内,它的特殊之处在于,除了能够像 BeanFactoryPostProcessor 那样修改已经注册的bean定义(BeanDefinition),还能向注册中心 BeanDefinitionRegistry 中动态地添加或移除bean定义。

BeanDefinitionRegistryPostProcessor 提供了一个核心方法:postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)。通过该方法,我们可以直接操作 BeanDefinitionRegistry,这是一个专门用于bean定义注册的中心接口。它允许我们直接注册新的bean定义、修改已有的bean定义或者完全移除某些bean定义。

使用这个接口的常见场景包括基于项目的特定条件动态地注册beans,例如,可能只在某些环境中需要的beans,或者基于配置选项动态地选择实现类。

BeanFactoryPostProcessor 的关键区别在于其执行时机。BeanDefinitionRegistryPostProcessor 的方法在所有其他 BeanFactoryPostProcessor 方法之前执行,这确保了它可以在其他处理器操作前先注册或修改bean定义。

总的来说,BeanDefinitionRegistryPostProcessor 提供了一种在Spring容器配置解析阶段动态介入的能力,允许我们在其他配置处理器介入之前,进行更为深入的bean定义的调整和优化。

2.2 BeanDefinitionRegistryPostProcessor 的执行时机

执行时机用一张流程图表示如下:

  1. 加载配置 : Spring从各种来源(如XML文件、Java配置、注解)加载配置信息。
  2. 解析配置 : 根据加载的配置,Spring创建对应的BeanDefinition
  3. 注册BeanDefinition : 解析完成后,Spring将这些BeanDefinition对象注册到BeanDefinitionRegistry中。
  4. 执行BeanDefinitionRegistryPostProcessor : 这个后置处理器提供了一个重要的扩展点,允许在所有BeanDefinition注册完毕后,但在Bean实例化之前进行一些操作。例如:注册新的BeanDefinition、修改或删除现有的BeanDefinition
  5. 执行BeanFactoryPostProcessor : 这个后置处理器提供了另一个扩展点,它主要允许查看或修改已经注册的BeanDefinition。例如,根据某些条件更改Bean的作用域或属性值。
  6. 实例化Bean : 这是将BeanDefinition转换为实际的Bean实例的过程。
  7. 依赖注入 : 在这一步,Spring 框架会按照 BeanDefinition 的描述为 bean 实例注入所需的依赖。
  8. Bean初始化 : 在所有依赖都注入后,特定的初始化方法(如通过@PostConstruct指定的)将会被调用,完成Bean的最后设置。
  9. 执行BeanPostProcessor的方法 : BeanPostProcessor提供了拦截的能力,允许在Bean初始化阶段结束之前和之后进行操作。
  10. Bean完全初始化 : 在此阶段,Bean 完全初始化并准备好被应用程序使用。

虚线解释:

  • 执行BeanDefinitionRegistryPostProcessor 到 执行BeanFactoryPostProcessor 之间的虚线:

注册/修改/删除BeanDefinition: 在执行 BeanDefinitionRegistryPostProcessor 的过程中,除了执行已定义的操作外,还提供了一个重要的扩展点,允许我们注册新的 BeanDefinition、修改或删除已有的 BeanDefinition。这为我们提供了一个机会在后续的 BeanFactoryPostProcessor 执行前改变或增强我们的 bean 定义。

  • 执行BeanFactoryPostProcessor 到 实例化Bean 之间的虚线:

查看/修改BeanDefinition: BeanFactoryPostProcessor 允许我们查看或修改已注册的 BeanDefinition。这意味着在 bean 实例化之前,我们还有最后一次机会修改 bean 的定义或属性。例如,根据某些运行时环境或条件更改 bean 的作用域。

2.3. 动态注册 Bean:BeanDefinitionRegistryPostProcessor 实践

假设有一个Fruit的抽象水果类,以及两个具体的水果类:AppleOrange。在最初,IOC容器中只注册了Apple,没有Orange。我们将使用BeanDefinitionRegistryPostProcessor来注册一个Orange的实例,然后利用BeanFactoryPostProcessor来为所有的Fruit实例设置属性。

  1. 声明Bean

首先,我们定义抽象类Fruit及其属性:

java 复制代码
package com.example.demo.bean;

public abstract class Fruit {
    protected String type;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}
java 复制代码
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Apple extends Fruit {
    @Override
    public String toString() {
        return "Apple{" + "type='" + type + '\'' + "}";
    }
}
java 复制代码
package com.example.demo.bean;

public class Orange extends Fruit {
    @Override
    public String toString() {
        return "Orange{" + "type='" + type + '\'' + '}';
    }
}

Orange类没有标注@Component注解,Spring的组件扫描功能默认不会为其创建bean,这个例子中会在OrangeRegisterPostProcessor 里动态创建。

  1. 编写后置处理器

使用后置处理器来注册Orange

java 复制代码
package com.example.demo.processor;

import com.example.demo.bean.Orange;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;


/**
 * OrangeRegisterPostProcessor是一个BeanDefinitionRegistryPostProcessor。
 * 它的主要作用是检查IOC容器中是否已经包含了名为"orange"的bean定义。
 * 如果没有,它会动态创建一个Orange类的bean定义并注册到容器中。
 */
@Component
public class OrangeRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        System.out.println("postProcessBeanDefinitionRegistry in OrangeRegisterPostProcessor started.");

        if (!registry.containsBeanDefinition("orange")) {
            BeanDefinition orangeDefinition = BeanDefinitionBuilder.genericBeanDefinition(Orange.class).getBeanDefinition();
            registry.registerBeanDefinition("orange", orangeDefinition);
        }
    }
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        System.out.println("postProcessBeanFactory in OrangeRegisterPostProcessor started.");
    }
}

为所有的Fruit实例设置属性:

java 复制代码
package com.example.demo.processor;

import com.example.demo.bean.Fruit;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;


/**
 * FruitTypeSetterPostProcessor是一个BeanFactoryPostProcessor。
 * 它的主要作用是为所有Fruit类型的bean(Apple和Orange)设置"type"属性。
 * 其中,属性的值与bean的名称相同。
 */
@Component
public class FruitTypeSetterPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        System.out.println("postProcessBeanFactory in FruitTypeSetterPostProcessor started.");

        String[] fruitNames = beanFactory.getBeanNamesForType(Fruit.class);
        for (String name : fruitNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
            beanDefinition.getPropertyValues().add("type", name);
        }
    }
}
  1. 测试运行
java 复制代码
package com.example.demo;

import com.example.demo.bean.Apple;
import com.example.demo.bean.Orange;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo");
        Apple apple = context.getBean(Apple.class);
        System.out.println(apple);
        Orange orange = context.getBean(Orange.class);
        System.out.println(orange);
    }
}

运行结果:

这段代码展示了如何使用BeanDefinitionRegistryPostProcessor来动态地注册beans和为其设置属性。

3. 三种后置处理器的对比

后置处理器 处理目标 执行时机 主要操作
BeanPostProcessor 针对已经实例化但未完全初始化的 bean 对象 在bean的初始化过程中,具体是在bean生命周期的初始化方法前后执行 主要用于在bean初始化前后进行操作,如修改bean属性、生成代理对象等。
BeanFactoryPostProcessor 主要针对BeanDefinition,即bean的配置元数据 所有的 BeanDefinition 已加载到 BeanDefinitionRegistry 且已完成注册。此时机允许我们在bean真正实例化和初始化之前,对其配置元数据(即 BeanDefinition)进行修改或添加操作 修改已注册的 BeanDefinition。此操作可能包括修改属性、改变类的定义或者进行其他任何与 BeanDefinition 相关的操作
BeanDefinitionRegistryPostProcessor 主要针对BeanDefinitionRegistry,该处理器可以处理来自各种配置源(如配置文件、Java配置等)的BeanDefinition 在所有 BeanDefinition 被加载和注册之后,但在其他 BeanFactoryPostProcessor 执行之前 向 BeanDefinitionRegistry 注册、修改或移除 BeanDefinition

4. 总结与洞见

4.1. BeanFactoryPostProcessor 与 BeanPostProcessor 的差异

BeanFactoryPostProcessorBeanPostProcessor 都是 Spring 框架中为了增强容器的处理能力而提供的扩展点。它们都可以对 Bean 进行定制化处理,但它们的关注点和应用时机不同。

  1. BeanFactoryPostProcessor:
  • 功能: 允许我们在 Spring 容器实例化任何 bean 之前读取 bean 的定义(bean 的元数据)并进行修改。

  • 作用时机: 它会在 BeanFactory 的标准初始化之后被调用,此时,所有的 bean 定义已经被加载到容器中,但还没有实例化任何 bean。此时我们可以添加、修改或移除某些 bean 的定义。

  • 常见应用: 动态修改 bean 的属性、改变 bean 的作用域、动态注册新的 bean 等。

  • 示例接口方法:void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

  1. BeanPostProcessor:
  • 功能: 允许我们在 Spring 容器实例化 bean 之后对 bean 进行处理,提供了一种机会在 bean 的初始化前后插入我们的自定义逻辑。

  • 作用时机: 它在 bean 的生命周期中的两个时间点被调用,即在自定义初始化方法(如 @PostConstruct, init-method)之前和之后。

  • 常见应用: 对特定的 bean 实例进行一些额外处理,如进行某种代理、修改 bean 的状态等。

  • 示例接口方法:Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; 和 Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

总结:

  • BeanFactoryPostProcessor 主要关注于整个容器的配置,允许我们修改 bean 的定义或元数据。它是容器级别的。

  • BeanPostProcessor 主要关注于 bean 的实例,允许我们在初始化前后对 bean 实例进行操作。它是 bean 级别的。

4.2. BeanFactoryPostProcessor 与 BeanDefinitionRegistryPostProcessor 的关系

BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor 都是 Spring 中提供的两个重要的扩展点,它们都允许我们在 Spring 容器启动过程中对 Bean 的定义进行定制处理。但它们的应用时机和功能上存在一些不同。

  1. BeanFactoryPostProcessor:
  • 功能: 允许我们在 Spring 容器实例化任何 bean 之前读取 bean 的定义 (BeanDefinition) 并进行修改。

  • 作用时机: 在所有的 bean 定义都被加载、但 bean 实例还未创建的时候执行。

  • 常见应用: 修改已加载到容器中的 bean 定义的属性,例如更改某个 bean 的作用域、属性值等。

  • 主要方法: void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

  1. BeanDefinitionRegistryPostProcessor:
  • 功能: 扩展了 BeanFactoryPostProcessor,提供了一个新的方法来修改应用程序的上下文的 bean 定义。此外,还可以动态注册新的 bean 定义。

  • 作用时机: 它也是在所有 bean 定义被加载后执行,但在 BeanFactoryPostProcessor 之前。

  • 常见应用: 动态注册新的 bean 定义、修改或移除已有的 bean 定义。

  • 主要方法: void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

总结:

  • BeanFactoryPostProcessor 主要是用来修改已经定义的 bean 定义,而不是注册新的 bean

  • BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor 的扩展,并提供了额外的能力来动态地注册、修改、移除 bean 定义。

Spring 容器的启动过程中,首先执行的是 BeanDefinitionRegistryPostProcessor 的方法,之后才是 BeanFactoryPostProcessor 的方法。

欢迎一键三连~

有问题请留言,大家一起探讨学习

----------------------Talk is cheap, show me the code-----------------------

相关推荐
sniper_fandc13 分钟前
SpringBoot系列—入门
java·spring boot·后端
先睡3 小时前
Redis的缓存击穿和缓存雪崩
redis·spring·缓存
Albert Edison6 小时前
【最新版】IntelliJ IDEA 2025 创建 SpringBoot 项目
java·spring boot·intellij-idea
Piper蛋窝7 小时前
深入 Go 语言垃圾回收:从原理到内建类型 Slice、Map 的陷阱以及为何需要 strings.Builder
后端·go
Bug退退退1238 小时前
RabbitMQ 高级特性之死信队列
java·分布式·spring·rabbitmq
六毛的毛9 小时前
Springboot开发常见注解一览
java·spring boot·后端
AntBlack9 小时前
拖了五个月 ,不当韭菜体验版算是正式发布了
前端·后端·python
31535669139 小时前
一个简单的脚本,让pdf开启夜间模式
前端·后端
uzong10 小时前
curl案例讲解
后端
开开心心就好10 小时前
免费PDF处理软件,支持多种操作
运维·服务器·前端·spring boot·智能手机·pdf·电脑