文章目录
- 1. 探索Spring的后置处理器(BeanPostProcessor)
- 1.1 BeanPostProcessor的设计理念
- 1.2 BeanPostProcessor的文档说明
- 2. BeanPostProcessor的使用
- 2.1 BeanPostProcessor的基础使用示例
- 2.2 利用BeanPostProcessor修改Bean的初始化结果的返回值
- 2.3 通过BeanPostProcessor实现Bean属性的动态修改
- 3. 深度剖析BeanPostProcessor的执行时机
- 3.1 后置处理器在Bean生命周期中的作用及执行时机
- 3.2 图解: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
接口定义了两个方法,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)
这个日志信息告诉我们,BeanPostProcessor
并没有应用到所有的bean
上,具体来说,没有应用到 ‘animalConfig
’ 这个bean
。这个信息的出现是因为在Spring
的初始化过程中,BeanPostProcessor
们会被提前实例化,并处理其他的bean
,但是这个过程需要注意,如果在BeanPostProcessor
实例化过程中需要用到其他bean
,那么这些bean
不会被所有的BeanPostProcessor
处理,只会被当前已经实例化的BeanPostProcessor
处理。
以这个例子来说,AnimalConfig bean
是一个配置类,它是在初始化MyBeanPostProcessor
之前就被实例化的,因此它没有被所有的BeanPostProcessor
处理,也就是说MyBeanPostProcessor
没有对这个配置类进行处理。我们可以不把MyBeanPostProcessor
实例化放在配置类,直接加上@Component
,这个红色提示日志就会消失。
如果这些话不理解,咱们先看完后面的内容,再回头看看这句话就简单多了。
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
的作用。
根据打印日志我们可以分析出
-
首先,
Bean Constructor Method Invoked!
表明Lion
的构造器被调用,创建了一个新的 Lion 实例。 -
接着,
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
实例。 -
然后,
postProcessBeforeInitialization Method Invoked!
说明MyBeanPostProcessor
的postProcessBeforeInitialization
方法被调用,这是在初始化Lion
实例之前。 -
@PostConstruct Method Invoked!
说明@PostConstruct
注解的方法被调用,这是在Bean
初始化之后,但是在Spring
执行任何进一步初始化之前。 -
afterPropertiesSet Method Invoked!
说明Spring
调用了InitializingBean
的afterPropertiesSet
方法 -
customInitMethod Method Invoked!
表示调用了Lion
实例的init-method
方法。 -
postProcessAfterInitialization Method Invoked!
说明MyBeanPostProcessor
的postProcessAfterInitialization
方法被调用,这是在初始化Lion
实例之后。
然后 Spring
完成了整个初始化过程。
- 主程序中手动调用了
Lion
实例的setter
方法,因此在Bean Setter Method Invoked! name: oh!!! My Bean set new name
可见,name
属性被设置了新的值"oh!!! My Bean set new name"
。
当容器准备关闭时:
-
@PreDestroy Method Invoked!
说明@PreDestroy
注解的方法被调用,这是在Bean
销毁之前。 -
destroy Method Invoked!
表示Lion
实例开始销毁。在这一步,Spring
调用了DisposableBean
的destroy
方法。 -
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
的实例化、属性赋值、生命周期方法的执行和后置处理器的调用。
欢迎一键三连~
有问题请留言,大家一起探讨学习
----------------------Talk is cheap, show me the code-----------------------