首页 > 其他分享 >Day6——Bean生命周期的扩展点:BeanPostProcessor

Day6——Bean生命周期的扩展点:BeanPostProcessor

时间:2023-12-13 10:32:13浏览次数:33  
标签:BeanPostProcessor bean Day6 Lion Bean import public

【摘要】 在本篇文章中,我们将深入探讨Spring框架中的重要组件——BeanPostProcessor。首先,我们将了解其设计理念和目标,然后通过实际的例子学习如何基础使用它,如何通过BeanPostProcessor改变Bean的初始化结果以及如何利用它修改Bean的属性。最后,我们将深入理解后置处理器在Bean生命周期中的作用和执行时机,帮助读者更好地理解和使用这个强大的工具。

在前一篇讲解生命周期的时候就可以讲解后置处理器了,但是内容比较多,还是分开来讲解。

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 接口定义了两个方法,postProcessBeforeInitializationpostProcessAfterInitialization

Day6——Bean生命周期的扩展点:BeanPostProcessor_System

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

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

Day6——Bean生命周期的扩展点:BeanPostProcessor_System_02


2. BeanPostProcessor的使用

2.1 BeanPostProcessor的基础使用示例

全部代码如下:

首先定义两个简单的BeanLionElephant

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;
    }
}

接着我们定义一个配置类,其中包含对LionElephant类和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();
    }
}

运行结果:

Day6——Bean生命周期的扩展点:BeanPostProcessor_System_03

以上代码在执行时,将先创建LionElephant对象,然后在初始化过程中和初始化后调用postProcessBeforeInitializationpostProcessAfterInitialization方法,打印出被处理的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应用上下文中可以存在多个BeanPostProcessorSpring本身就提供了很多内置的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;
    }
}

运行结果:

Day6——Bean生命周期的扩展点:BeanPostProcessor_初始化_04

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

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

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

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;
    }
}

我们运行看结果

Day6——Bean生命周期的扩展点:BeanPostProcessor_spring_05

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

Day6——Bean生命周期的扩展点:BeanPostProcessor_spring_06

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

Day6——Bean生命周期的扩展点:BeanPostProcessor_spring_07

从我这个调试图中可以看到,如果postProcessBeforeInitialization返回nullSpring仍然用原始的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();
    }
}

运行结果:

Day6——Bean生命周期的扩展点:BeanPostProcessor_System_08

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


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

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

在这个例子中,我们将创建一个名为LionElephant 的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的作用。

Day6——Bean生命周期的扩展点:BeanPostProcessor_System_09

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

  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 完成了整个初始化过程。

  1. 主程序中手动调用了 Lion 实例的 setter 方法,因此在 Bean Setter Method Invoked! name: oh!!! My Bean set new name 可见,name 属性被设置了新的值 "oh!!! My Bean set new name"

当容器准备关闭时:

  1. @PreDestroy Method Invoked! 说明 @PreDestroy 注解的方法被调用,这是在 Bean 销毁之前。
  2. destroy Method Invoked! 表示 Lion 实例开始销毁。在这一步,Spring 调用了 DisposableBean 的 destroy 方法。
  3. 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的实例化、属性赋值、生命周期方法的执行和后置处理器的调用。

Day6——Bean生命周期的扩展点:BeanPostProcessor_System_10


标签:BeanPostProcessor,bean,Day6,Lion,Bean,import,public
From: https://blog.51cto.com/u_16299440/8798772

相关文章

  • Java-SpringBean的生命周期
    Java-SpringBean的生命周期简单版实例化(Instantiation):当Spring容器启动时,它会检查配置文件或注解,然后实例化所有在配置中声明的Bean。这是通过构造函数或工厂方法进行的。属性设置(PopulationofProperties):容器实例化Bean后,会通过依赖注入或者setter方法将配置的......
  • spring bean的生命周期
    springbean的生命周期分为六个阶段阶段一:容器启动阶段主要完成了扫描、实例化beanDefinitino对象、注册BeanPostProcessor、验证beanDefinition是否合格阶段二:Bean的实例化阶段主要推断实例化方式、实例化对象阶段三:bean的属性注入提前暴露、循环依赖做支持、查找注入信息......
  • spring中bean是什么
    spring中bean是什么在Spring框架中,"Bean"是一个概念,它表示由SpringIoC容器管理的对象。Bean是Spring框架的基本构建块,它可以是一个Java对象、一个组件、一个服务,或者是你的应用程序中的任何一个对象。具体来说,Spring的Bean具有以下特征:由容器管理:Bean由SpringIoC容器负责......
  • Spring Bean的创建和初始化流程
    AbstractApplicatContext【refresh】点击到该方法中,直接看到方法最后的beanFactory.preInstantiateSingletons()方法:DefaultListableBeanFactory【preInstantiateSingletons】在 preInstantiateSingletons() 方法中,可以明显看到拿到了 beanDefinitionNames 列表,而这个......
  • JSON提取器id+BeanShell断言
    JSON提取data中的值: 用提取得值${id}再继续执行: 利用BeanShell断言:importorg.json.*;Stringresponsedata=prev.getResponseDataAsString();JSONObjectdata_obj=newJSONObject(responsedata);Stringid=data_obj.get("data").get("id").toString();Strings......
  • Spring的FactoryBean接口的作用
    FactoryBean是Spring提供的一个接口,通过实现这个接口可以定制实例化bean。FactoryBean接口对于Spring框架来说占有重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。Spring为什么使用FactoryBean一......
  • is not eligible for getting processed by all BeanPostProcessors 问题解决
    问题在做Springboot项目时遇到如下报错18.684INFOo.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker:350restartedMainBean'org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration'oftype[org.apache.rocket......
  • Spring入门04Bean的配置、Bean的实例化、Bean的生命周期、依赖注入方式、依赖自动装配
    bean配置bean基础配置 bean别名配置 bean作用范围配置  bean实例化bean是如何创建的bean实例化bean本质上就是对象,创建bean使用构造方法完成 实例化bean的三种方式 bean生命周期bean生命周期控制   依赖注入方式依赖注入的两种方式......
  • Spring的Bean工厂的后置处理器BeanFactoryPostProcessor执行流程
    BeanFactoryPostProcessor作用BeanFactoryPostProcessor是Spring框架中的一个重要接口,用于在BeanFactory加载Bean定义之后、实例化Bean之前对BeanFactory进行自定义修改和扩展。它允许开发人员在Spring容器加载配置文件并创建Bean实例之前对Bean定义进行操作,例如修改属性值、添加......
  • Spring的后置处理器BeanPostProcessor接口的执行流程
    BeanPostProcessor的设计目标主要是提供一种扩展机制,让开发者可以在SpringBean的初始化阶段进行自定义操作。这种设计理念主要体现了Spring的一种重要原则,即“开放封闭原则”。开放封闭原则强调软件实体(类、模块、函数等等)应该对于扩展是开放的,对于修改是封闭的。在这里,Spring容......