Bean生命周期的扩展点:Bean Post Processor

这篇具有很好参考价值的文章主要介绍了Bean生命周期的扩展点:Bean Post Processor。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

摘要:在本篇文章中,我们将深入探讨Spring框架中的重要组件——BeanPostProcessor。首先,我们将了解其设计理念和目标,然后通过实际的例子学习如何基础使用它,如何通过BeanPostProcessor改变Bean的初始化结果以及如何利用它修改Bean的属性。

本文分享自华为云社区《Spring高手之路6——Bean生命周期的扩展点:BeanPostProcessor》,作者:砖业洋__ 。

1. 探索Spring的后置处理器(BeanPostProcessor)

1.1 BeanPostProcessor的设计理念

BeanPostProcessor的设计目标主要是提供一种扩展机制,让开发者可以在Spring Bean的初始化阶段进行自定义操作。这种设计理念主要体现了Spring的一种重要原则,即“开放封闭原则”。开放封闭原则强调软件实体(类、模块、函数等等)应该对于扩展是开放的,对于修改是封闭的。在这里,Spring容器对于Bean的创建、初始化、销毁等生命周期进行了管理,但同时开放了BeanPostProcessor这种扩展点,让开发者可以在不修改Spring源码的情况下,实现对Spring Bean生命周期的自定义操作,这种设计理念大大提升了Spring的灵活性和可扩展性。

BeanPostProcessor不是Spring Bean生命周期的一部分,但它是在Spring Bean生命周期中起重要作用的组件。

1.2 BeanPostProcessor的文档说明

我们来看看这个方法的文档注释,从图中可以看到,BeanPostProcessor 接口定义了两个方法,postProcessBeforeInitialization和postProcessAfterInitialization

postProcessBeforeInitialization方法会在任何bean初始化回调(如InitializingBean的afterPropertiesSet方法或者自定义的init-method)之前被调用。也就是说,这个方法会在bean的属性已经设置完毕,但还未进行初始化时被调用。

postProcessAfterInitialization方法在任何bean初始化回调(比如InitializingBean的afterPropertiesSet或者自定义的初始化方法)之后被调用。这个时候,bean的属性值已经被填充完毕。返回的bean实例可能是原始bean的一个包装。

2. BeanPostProcessor的使用

2.1 BeanPostProcessor的基础使用示例

全部代码如下:

首先定义两个简单的Bean:Lion和Elephant

Lion.java

package com.example.demo.bean;
public class Lion {
 private String name;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
}

Elephant.java

package com.example.demo.bean;
public class Elephant {
 private String name;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
}

然后定义一个简单的BeanPostProcessor,它只是打印出被处理的Bean的名字:

package com.example.demo.processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("Before initialization: " + beanName);
 return bean;
 }
 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("After initialization: " + beanName);
 return bean;
 }
}

接着我们定义一个配置类,其中包含对Lion、Elephant类和MyBeanPostProcessor类的Bean定义:

package com.example.demo.configuration;
import com.example.demo.bean.Elephant;
import com.example.demo.bean.Lion;
import com.example.demo.processor.MyBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AnimalConfig {
 @Bean
 public Lion lion() {
 return new Lion();
 }
 @Bean
 public Elephant elephant() {
 return new Elephant();
 }
 @Bean
 public MyBeanPostProcessor myBeanPostProcessor() {
 return new MyBeanPostProcessor();
 }
}

最后,我们在主程序中创建ApplicationContext对象:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
 public static void main(String[] args) {
 ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
 ((AnnotationConfigApplicationContext)context).close();
 }
}

运行结果:

以上代码在执行时,将先创建Lion和Elephant对象,然后在初始化过程中和初始化后调用postProcessBeforeInitialization和postProcessAfterInitialization方法,打印出被处理的Bean的名字。

细心的小伙伴可能观察到这里有红色日志
信息: Bean 'animalConfig' of type [com.example.demo.configuration.AnimalConfig$$EnhancerBySpringCGLIB$$ee4adc7e] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

在Spring中,BeanPostProcessor是被特殊处理的,它们会在其他普通Bean之前被实例化和初始化,这样设计的原因是BeanPostProcessor的存在可以影响其他Bean的创建和初始化过程。 Spring应用上下文中可以存在多个BeanPostProcessor,Spring本身就提供了很多内置的BeanPostProcessor。

但是,如果在初始化BeanPostProcessor的过程中需要依赖其他的Bean,那么这些被依赖的Bean会先于后置处理器进行初始化。然而,由于这些被依赖的Bean是在该BeanPostProcessor初始化完成之前就已经进行了初始化,它们就会错过这个BeanPostProcessor的处理。在这个例子中,MyBeanPostProcessor就是这样的一个BeanPostProcessor,而"animalConfig"是它所依赖的Bean。所以这个日志信息就是说,'animalConfig'这个Bean在初始化的时候,没有被所有的BeanPostProcessor处理,这里它无法得到MyBeanPostProcessor的处理。

我们只需要把实例化过程直接交给Spring容器来管理,而不是在配置类中手动进行实例化,就可以消除这个提示信息,也就是在MyBeanPostProcessor上加@Component即可。

在第3节的例子中就使用了@Component处理这个MyBeanPostProcessor,这个提示就消失了。

2.2 利用BeanPostProcessor修改Bean的初始化结果的返回值

还是上面的例子,我们只修改一下MyBeanPostProcessor 类的方法后再次运行

package com.example.demo.processor;
import com.example.demo.bean.Elephant;
import com.example.demo.bean.Lion;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("Before initialization: " + bean);
 if (bean instanceof Lion) {
 return new Elephant();
 }
 return bean;
 }
 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("After initialization: " + bean);
 return bean;
 }
}

运行结果:

BeanPostProcessor的两个方法都可以返回任意的Object,这意味着我们可以在这两个方法中更改返回的bean。例如,如果我们让postProcessBeforeInitialization方法在接收到Lion实例时返回一个新的Elephant实例,那么我们将会看到Lion实例变成了Elephant实例。

那既然BeanPostProcessor的两个方法都可以返回任意的Object,那我搞点破坏返回null会怎么样,会不会因为初始化bean为null而导致异常呢?

答案是不会的,我们来看一下:

package com.example.demo.processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("Before initialization: " + bean);
 return null;
 }
 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("After initialization: " + bean);
 return bean;
 }
}

我们运行看结果

结果发现还是正常初始化的bean类型,不会有任何改变,我们继续调试看看是为什么

我们通过堆栈帧看到调用postProcessBeforeInitialization方法的上一个方法是applyBeanPostProcessorsBeforeInitialization,双击点开看一看这个方法

从我这个调试图中可以看到,如果postProcessBeforeInitialization返回null,Spring仍然用原始的bean进行后续的处理,同样的逻辑在postProcessAfterInitialization也是一样。这就是为什么我们在BeanPostProcessor类的方法中返回null,原始bean实例还是存在的原因。

2.3 通过BeanPostProcessor实现Bean属性的动态修改

来看看是怎么拦截 bean 的初始化的

全部代码如下:

首先,我们定义一个Lion类:

public class Lion {
 private String name;
 public Lion() {
 this.name = "Default Lion";
 }
 public Lion(String name) {
 this.name = name;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 @Override
 public String toString() {
 return "Lion{" + "name='" + name + '\'' + '}';
 }
}

接下来,我们定义一个BeanPostProcessor,我们称之为MyBeanPostProcessor :

package com.example.demo.processor;
import com.example.demo.bean.Lion;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("Bean的初始化之前:" + bean);
 return bean;
 }
 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("Bean的初始化之后:" + bean);
 if (bean instanceof Lion) {
 ((Lion) bean).setName("Simba");
 }
 return bean;
 }
}

然后我们定义一个配置类,其中包含对Lion类的Bean定义和对MyBeanPostProcessor 类的Bean定义:

package com.example.demo.configuration;
import com.example.demo.bean.Lion;
import com.example.demo.processor.MyBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AnimalConfig {
 @Bean
 public Lion lion() {
 return new Lion();
 }
 @Bean
 public MyBeanPostProcessor myBeanPostProcessor() {
 return new MyBeanPostProcessor();
 }
}

最后,我们在主程序中创建ApplicationContext对象,并获取Lion对象:

package com.example.demo;
import com.example.demo.bean.Lion;
import com.example.demo.configuration.AnimalConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
 public static void main(String[] args) {
 ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
 Lion lion = context.getBean("lion", Lion.class);
 System.out.println(lion);
 ((AnnotationConfigApplicationContext)context).close();
 }
}

运行结果:

上面代码在执行时,先创建一个Lion对象,然后在初始化过程中和初始化后调用postProcessBeforeInitialization和postProcessAfterInitialization方法,修改Lion的名字为"Simba",最后在主程序中输出Lion对象,显示其名字为"Simba"。

3. 深度剖析BeanPostProcessor的执行时机

3.1 后置处理器在Bean生命周期中的作用及执行时机

在这个例子中,我们将创建一个名为Lion和Elephant 的Bean,它会展示属性赋值和生命周期的各个步骤的执行顺序。同时,我们还将创建一个BeanPostProcessor来打印消息并显示它的执行时机。

全部代码如下:

首先,我们定义我们的Lion:

package com.example.demo.bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
public class Lion implements InitializingBean, DisposableBean {
 private String name;
 private Elephant elephant;
 public Lion() {
 System.out.println("1. Bean Constructor Method Invoked!");
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 System.out.println("2. Bean Setter Method Invoked! name: " + name);
 }
 /**
     * setter注入
     * @param elephant
     */
 @Resource
 public void setElephant(Elephant elephant) {
 this.elephant = elephant;
 System.out.println("2. Bean Setter Method Invoked! elephant: " + elephant);
 }
 @PostConstruct
 public void postConstruct() {
 System.out.println("4. @PostConstruct Method Invoked!");
 }
 @Override
 public void afterPropertiesSet() throws Exception {
 System.out.println("5. afterPropertiesSet Method Invoked!");
 }
 public void customInitMethod() {
 System.out.println("6. customInitMethod Method Invoked!");
 }
 @PreDestroy
 public void preDestroy() {
 System.out.println("8. @PreDestroy Method Invoked!");
 }
 @Override
 public void destroy() throws Exception {
 System.out.println("9. destroy Method Invoked!");
 }
 public void customDestroyMethod() {
 System.out.println("10. customDestroyMethod Method Invoked!");
 }
}

创建Lion所依赖的Elephant

package com.example.demo.bean;
import org.springframework.stereotype.Component;
@Component
public class Elephant {
 private String name;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
}

然后,我们定义一个简单的BeanPostProcessor:

package com.example.demo.processor;
import com.example.demo.bean.Lion;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 if(bean instanceof Lion) {
 System.out.println("3. postProcessBeforeInitialization Method Invoked!");
 }
 return bean;
 }
 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 if(bean instanceof Lion) {
 System.out.println("7. postProcessAfterInitialization Method Invoked!");
 }
 return bean;
 }
}

创建一个配置类AnimalConfig

package com.example.demo.configuration;
import com.example.demo.bean.Lion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AnimalConfig {
 @Bean(initMethod = "customInitMethod", destroyMethod = "customDestroyMethod")
 public Lion lion() {
 Lion lion = new Lion();
 lion.setName("my lion");
 return lion;
 }
}

主程序:

package com.example.demo;
import com.example.demo.bean.Lion;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
 public static void main(String[] args) {
 System.out.println("容器初始化之前...");
 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example");
 System.out.println("容器初始化完成");
 Lion bean = context.getBean(Lion.class);
 bean.setName("oh!!! My Bean set new name");
 System.out.println("容器准备关闭...");
 context.close();
 System.out.println("容器已经关闭");
 }
}

控制台上看到所有的方法调用都按照预期的顺序进行,这可以更好地理解Bean属性赋值和生命周期以及BeanPostProcessor的作用。

根据打印日志我们可以分析出

  1. 首先,Bean Constructor Method Invoked! 表明 Lion 的构造器被调用,创建了一个新的 Lion 实例。
  2. 接着,Bean Setter Method Invoked! name: my lion 和 Bean Setter Method Invoked! elephant: com.example.demo.bean.Elephant@7364985f 说明 Spring 对 Lion 实例的依赖注入。在这一步,Spring 调用了 Lion 的 setter 方法,为 name 属性设置了值 “my lion”,同时为 elephant 属性注入了一个 Elephant 实例。
  3. 然后,postProcessBeforeInitialization Method Invoked! 说明 MyBeanPostProcessor 的 postProcessBeforeInitialization 方法被调用,这是在初始化 Lion 实例之前。
  4. @PostConstruct Method Invoked! 说明 @PostConstruct 注解的方法被调用,这是在 Bean 初始化之后,但是在 Spring 执行任何进一步初始化之前。
  5. afterPropertiesSet Method Invoked! 说明 Spring 调用了 InitializingBean 的 afterPropertiesSet 方法
  6. customInitMethod Method Invoked! 表示调用了 Lion 实例的 init-method 方法。
  7. postProcessAfterInitialization Method Invoked! 说明 MyBeanPostProcessor 的 postProcessAfterInitialization 方法被调用,这是在初始化 Lion 实例之后。
    然后 Spring 完成了整个初始化过程。
  8. 主程序中手动调用了 Lion 实例的 setter 方法,因此在 Bean Setter Method Invoked! name: oh!!! My Bean set new name 可见,name 属性被设置了新的值 "oh!!! My Bean set new name"。
    当容器准备关闭时:
  9. @PreDestroy Method Invoked! 说明 @PreDestroy 注解的方法被调用,这是在 Bean 销毁之前。
  10. destroy Method Invoked! 表示 Lion 实例开始销毁。在这一步,Spring 调用了 DisposableBean 的 destroy 方法。
  11. customDestroyMethod Method Invoked! 表示 Lion 实例开始销毁,调用了Lion 实例的 destroy-method 方法。

最后,Spring 完成了整个销毁过程,容器关闭。

这个日志提供了 Spring Bean 生命周期的完整视图,显示了从创建到销毁过程中的所有步骤。

注意:DisposableBean 的 destroy 方法和 destroy-method 方法调用,这个销毁过程不意味着bean实例就被立即从内存中删除了,Java的垃圾收集机制决定了对象什么时候被从内存中删除。Spring容器无法强制进行这个操作,比如解除bean之间的关联和清理缓存,这并不是Spring在销毁bean时会做的,而是由Java的垃圾回收器在一个对象不再被引用时做的事情。

BeanPostProcessor 的执行顺序是在 Spring Bean 的生命周期中非常重要的一部分。例如,如果一个 Bean 实现了 InitializingBean 接口,那么 afterPropertiesSet 方法会在所有的 BeanPostProcessor 的 postProcessBeforeInitialization 方法之后调用,以确保所有的前置处理都完成了。同样,BeanPostProcessor 的 postProcessAfterInitialization 方法会在所有的初始化回调方法之后调用,以确保 Bean 已经完全初始化了。

我们可以注册多个 BeanPostProcessor。在这种情况下,Spring 会按照它们的 Ordered 接口或者 @Order 注解指定的顺序来调用这些后置处理器。如果没有指定顺序,那么它们的执行顺序是不确定的。

3.2 图解:Bean生命周期与后置处理器的交互时序

综合上面的执行结果,我们来总结一下,下面是Spring Bean生命周期的时序图,它详细地描绘了Spring Bean从实例化到准备使用的整个过程,包括Bean的实例化、属性赋值、生命周期方法的执行和后置处理器的调用。

 

点击关注,第一时间了解华为云新鲜技术~文章来源地址https://www.toymoban.com/news/detail-504877.html

到了这里,关于Bean生命周期的扩展点:Bean Post Processor的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Bean 的生命周期

    观前提示:本篇博客演示使用的 IDEA 版本为2021.3.3版本,使用的是Java8(又名jdk1.8) 电脑使用的操作系统版本为 Windows 10 目录 Spring 的执行流程 1. 启动容器 2. 根据配置完成 Bean 的初始化 3. 注册 Bean 对象到容器中 4. 装配 Bean 的属性 Bean 的生命周期 Bean 的生命周期代码演示 在看 Bean

    2024年02月09日
    浏览(40)
  • 【Spring】—— bean生命周期

    1、初始化容器 1)创建对象(分配内存) 2)执行构造方法 3)执行属性注入(set操作) 4)执行bean初始化方法 2、使用bean 1)执行业务操作 3、关闭/销毁容器 1)执行bean销毁方法

    2024年02月02日
    浏览(46)
  • Spring bean 生命周期

    在互联网领域中,Spring框架扮演着重要的角色。作为一个开源的Java应用程序开发框架,Spring提供了一种灵活而强大的方式来构建可扩展的应用程序。Spring框架中的一个重要概念是Bean,它是Spring应用程序的基本构建块之一。了解Spring Bean的生命周期对于充分利用Spring框架的功能

    2024年02月11日
    浏览(45)
  • 【Spring】Bean的作用域与生命周期详情:请简述Spring的执行流程并分析Bean的生命周期?

     我们都知道,Spring框架为开发人员提供了很多便捷,这使得开发人员能够更加专注于应用程序的核心业务逻辑,而不需要花费大量时间和精力在技术细节上。作为一个包含众多工具方法的IoC容器,存取JavaBean是其极为重要的一个环节。本文就对Spring中的Bean的作用域和生命周

    2024年02月12日
    浏览(48)
  • Spring -- Bean的生命周期

    Spring容器在进行实例化时,会将xml配置的bean的信息封装成一个BeanDefinition对象,Spring根据BeanDefinition来创建Bean对象,里面有很多的属性用来描述Bean   BeanDefinition 中几个重要方法如下 beanClassName:bean 的类名 initMethodName:初始化方法名称 properryValues:bean 的属性值 scope:作用域

    2024年02月15日
    浏览(53)
  • Bean的生命周期及演示

    概念 Bean的生命周期是指一个Bean对象从创建到销毁的整个存在过程。 Bean生命周期组成: 1.实例化Bean (为Bean分配内存空间) 2.属性注入 (Bean注入和装配) 3.Bean的初始化 各种通知:如 BeanNameWare、BeanFactoryAware、ApplicationContextAware 的接口方法。 初始化前置方法 执行初始化方法

    2024年02月04日
    浏览(47)
  • Spring Bean的生命周期

    Bean生命周期的整个执行过程描述如下: 1、根据配置情况调用Bean构造方法或工厂方法实例化 Bean。 2、利用依赖注入完成Bean中所有属性值的配置注入。 3、如果Bean 实现了BeanNameAware 接口,则 Spring调用Bean的setBeanName()方法传入当前Bean的id值。 4、如果Bean实现了BeanFactoryAware 接口

    2023年04月22日
    浏览(61)
  • 详解Spring Bean的生命周期

    Spring Bean的生命周期包括以下阶段: 1. 实例化Bean 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。 对于ApplicationContext容器,当容器启动结束后,便实例化所有的bean。

    2024年02月14日
    浏览(50)
  • Spring源码:Bean生命周期(三)

    在之前的文章中,我们已经对 bean 的准备工作进行了讲解,包括 bean 定义和 FactoryBean 判断等。在这个基础上,我们可以更加深入地理解 getBean 方法的实现逻辑,并在后续的学习中更好地掌握 createBean 方法的实现细节。 讲解getBean方法之前,我们先来看看他有几种常见的用法:

    2024年02月02日
    浏览(45)
  • Spring(11) Bean的生命周期

    首先,为什么要学习 Spring 中 Bean 的生命周期呢? 虽然不了解 Bean 的生命周期,并不影响日常工作中的开发。但是如果我们了解了 Bean 的生命周期,可以帮助我们更好地掌握 Spring 框架,并且能够让我们更好地去理解 Spring 容器是如何管理和创建 Bean 示例的。如果以后遇到 B

    2024年02月14日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包