Spring高手之路13

您所在的位置:网站首页 bean和类的区别 Spring高手之路13

Spring高手之路13

2024-07-15 09:52:52| 来源: 网络整理| 查看: 265

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实例化之前完成。

定义bean

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

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

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

代码语言:javascript复制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); } } } }运行测试

启动Spring容器,查看结果:

代码语言:javascript复制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的属性。

替代方法

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

代码语言:javascript复制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; } }

运行结果:

BeanPostProcessor 与 BeanFactoryPostProcessor 的对比2. BeanDefinitionRegistryPostProcessor 深入探究2.1 解读 BeanDefinitionRegistryPostProcessor

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

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

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

制定书单:图书馆先列出了所有想要的书的名称和作者,但还没有实际购买书籍。在Spring中,这就类似于创建BeanDefinition。在这个步骤后,但在图书馆真正购买书籍之前,假设图书馆收到了一个特别的捐赠列表(BeanDefinitionRegistryPostProcessor)。这个捐赠列表允许图书馆在正式购买书籍之前添加或修改书单。在Spring中,这是使用BeanDefinitionRegistryPostProcessor在实际的bean实例化之前修改bean定义的时机。按书单采购:此时,图书馆会按照更新后的书单进行购书。这个过程在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 的执行时机

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

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

声明Bean

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

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

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

编写后置处理器

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

代码语言:javascript复制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实例设置属性:

代码语言:javascript复制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); } } }测试运行代码语言:javascript复制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 进行定制化处理,但它们的关注点和应用时机不同。

BeanFactoryPostProcessor:功能: 允许我们在 Spring 容器实例化任何 bean 之前读取 bean 的定义(bean 的元数据)并进行修改。作用时机: 它会在 BeanFactory 的标准初始化之后被调用,此时,所有的 bean 定义已经被加载到容器中,但还没有实例化任何 bean。此时我们可以添加、修改或移除某些 bean 的定义。常见应用: 动态修改 bean 的属性、改变 bean 的作用域、动态注册新的 bean 等。示例接口方法:void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;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 的定义进行定制处理。但它们的应用时机和功能上存在一些不同。

BeanFactoryPostProcessor:功能: 允许我们在 Spring 容器实例化任何 bean 之前读取 bean 的定义 (BeanDefinition) 并进行修改。作用时机: 在所有的 bean 定义都被加载、但 bean 实例还未创建的时候执行。常见应用: 修改已加载到容器中的 bean 定义的属性,例如更改某个 bean 的作用域、属性值等。主要方法: void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;BeanDefinitionRegistryPostProcessor:功能: 扩展了 BeanFactoryPostProcessor,提供了一个新的方法来修改应用程序的上下文的 bean 定义。此外,还可以动态注册新的 bean 定义。作用时机: 它也是在所有 bean 定义被加载后执行,但在 BeanFactoryPostProcessor 之前。常见应用: 动态注册新的 bean 定义、修改或移除已有的 bean 定义。主要方法: void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

总结:

BeanFactoryPostProcessor 主要是用来修改已经定义的 bean 定义,而不是注册新的 bean。BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor 的扩展,并提供了额外的能力来动态地注册、修改、移除 bean 定义。

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

欢迎一键三连~

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

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



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭