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

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

主程序:

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java