写点什么

认识 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor

  • 2023-09-01
    广东
  • 本文字数:8507 字

    阅读完需:约 28 分钟

认识BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor

本文分享自华为云社区《Spring高手之路13——BeanFactoryPostProcessor与BeanDefinitionRegistryPos》,作者: 砖业洋__ 。


在 Spring 框架中,BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 是两个重要的后置处理器,它们在容器的启动过程中起到了至关重要的作用。本文深入探讨了这两者的定义、功能、执行时机以及如何在实际项目中使用它们。同时,通过对比分析,为读者揭示了它们与其他后置处理器之间的差异。

1. BeanFactoryPostProcessor 概览

1.1 解读 BeanFactoryPostProcessor


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


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

1.2. 如何使用 BeanFactoryPostProcessor


来看看 BeanFactoryPostProcessor 能如何影响 BeanDefinition。


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


1.定义 bean


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


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;
}
}
package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Blue extends Tint {
@Override
public String toString() {
return "Blue{" + "label='" + label + '\'' + "}";
}
}
package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Yellow extends Tint {
@Override
public String toString() {
return "Yellow{" + "label='" + label + '\'' + "}";
}
}
复制代码


2.创建后置处理器


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


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);
}
}
}
}
复制代码


3.运行测试


启动 Spring 容器,查看结果:


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 的属性。



4.替代方法


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


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;
}
}
复制代码


运行结果:



5.BeanPostProcessor 与 BeanFactoryPostProcessor 的对比


2. BeanDefinitionRegistryPostProcessor 深入探究

2.1 解读 BeanDefinitionRegistryPostProcessor


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


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


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


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

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

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


更佳专业化的描述如下


BeanDefinitionRegistryPostProcessor 是 Spring 中的一个高级扩展接口,继承自 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 的抽象水果类,以及两个具体的水果类:Apple 和 Orange。在最初,IOC 容器中只注册了 Apple,没有 Orange。我们将使用 BeanDefinitionRegistryPostProcessor 来注册一个 Orange 的实例,然后利用 BeanFactoryPostProcessor 来为所有的 Fruit 实例设置属性。


1.声明 Bean


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


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;
}
}
package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Apple extends Fruit {
@Override
public String toString() {
return "Apple{" + "type='" + type + '\'' + "}";
}
}
package com.example.demo.bean;
public class Orange extends Fruit {
@Override
public String toString() {
return "Orange{" + "type='" + type + '\'' + '}';
}
}
复制代码


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


2.编写后置处理器


使用后置处理器来注册 Orange:


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 实例设置属性:


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);
}
}
}
复制代码


3.测试运行


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. 三种后置处理器的对比


4. 总结与洞见

4.1. BeanFactoryPostProcessor 与 BeanPostProcessor 的差异


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


1.BeanFactoryPostProcessor:


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

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

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

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


2.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 的关系


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


1.BeanFactoryPostProcessor:


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

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

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

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


2.BeanDefinitionRegistryPostProcessor:


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

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

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

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


总结:


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

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


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

号外!



华为将于 2023 年 9 月 20-22 日,在上海世博展览馆和上海世博中心举办第八届华为全联接大会(HUAWEICONNECT 2023)。本次大会以“加速行业智能化”为主题,邀请思想领袖、商业精英、技术专家、合作伙伴、开发者等业界同仁,从商业、产业、生态等方面探讨如何加速行业智能化。


我们诚邀您莅临现场,分享智能化的机遇和挑战,共商智能化的关键举措,体验智能化技术的创新和应用。您可以:


  • 在 100+场主题演讲、峰会、论坛中,碰撞加速行业智能化的观点

  • 参观 17000 平米展区,近距离感受智能化技术在行业中的创新和应用

  • 与技术专家面对面交流,了解最新的解决方案、开发工具并动手实践

  • 与客户和伙伴共寻商机


感谢您一如既往的支持和信赖,我们热忱期待与您在上海见面。


大会官网:https://www.huawei.com/cn/events/huaweiconnect


欢迎关注“华为云开发者联盟”公众号,获取大会议程、精彩活动和前沿干货。


点击关注,第一时间了解华为云新鲜技术~

发布于: 2023-09-01阅读数: 4
用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
认识BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor_开发_华为云开发者联盟_InfoQ写作社区