深入了解Spring中Bean的生命周期的各个阶段
- 前提概要
- Bean的生命周期的定义层面
- BeanFactory、ApplicationContext不同的Bean生命周期
- Bean作用域于对生命周期的影响
- Bean生命周期扩展接口、方法的分类
- Bean的生命周期
- 如何记住Bean生命周期的生命阶段?
- Bean生命周期接口的分析
- Bean生命周期的代码实现分析
- 生命周期阶段的模拟
- 代码模拟
- 相关问题
- init-method、@ProConstruct、@Bean(initMethod = “xxxMethod”)、initializingBean接口的区别
- BeanFactoryPostProcessor的方法和InstantiationAwareBeanPostProcessor的前置方法是否有重叠实现之处?
- BeanFactoryPostProcessor和BeanPostProcessor的区别?
- 当Bean依赖其他Bean的时候,生命周期阶段又是怎么样执行的?
- 参考资料
前提概要
Bean的生命周期的定义层面
在Spring中,可以从两个层面去定义Bean的生命周期:
- 第一层面是Bean的作用范围
- 第二层面是实例化Bean时所经历的一系列阶段
Bean的生命周期是什么意思?
意思就是说从Bean的诞生到销毁整个过程中,都会固定做哪些动作。而这些动作就构成了Bean的完整的生命周期。这些动作是有顺序的,就像是一条调用链条,对Bean进行层层加工,只有做完一个动作,才会轮到下一个动作。
BeanFactory、ApplicationContext不同的Bean生命周期
BeanFactory与ApplicationContext的Bean的生命周期基本相同,但还是有一些不一样的地方,总的来说ApplicationContext的Bean生命周期囊括了BeanFactory的Bean生命周期,所以之后的讲解,没有特殊提示,我们默认都是对ApplicationContext的Bean生命周期进行讲解
区别:
-
ApplicationContext的Bean生命周期多了一个
ApplicationContextAware
的阶段,就是实现了ApplicationContextAware接口,我们就可以通过setApplicationContext为该Bean获取到ApplicationContext -
此外,如果ApplicationContext的Bean生命周期还多了工厂后置处理器阶段(
BeanFactoryPostProcessor
),通过实现该接口,我们可以在Bean实例化之前,对Bean的元数据BeanDefinition进行修改 -
另外,ApplicationContext会通过反射机制自动识别定义的
BeanFacotryPostProcessor
、InstantiationAwareBeanPostProcessor
、BeanPostProcessor
等Bean后置处理器,并自动将它们注册到应用上下文中。而BeanFactory则要在代码中手工调用addBeanPostProcessor()方法进行注册
Bean作用域于对生命周期的影响
Bean的作用域是会对Bean的生命周期产生部分影响的,Bean的作用域有:
作用域 | 说明 |
---|---|
singleton | 单例模式 |
prototype | 该作用域使用原型设计模式,每个请求都是一个新的Bean,而非单例 |
request | 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
session | 该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。 |
global-session | 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
我们这里只重点说明singleton和prototype的影响,因为其他的三种也不常用:
看图说话,我们可以看到再Bean的生命周期中,Bean已经被BeanPostProcessor处理了之后会进行准备就绪状态,而之后要看该Bean的作用域是singleton还是prototype来决定之后的流程:
- 如果是singleton,则按照正常的生命周期走完,直到被销毁
- 如果是prototype,将不再继续Bean的生命周期,而是将其交给用户管理,不再属于Spring的管理范畴,也不会被Spring销毁,而是交给虚拟机gc
Bean生命周期扩展接口、方法的分类
这里引用《精通Spring 4.x 企业应用开发实践》书中的分类
在整个Bean生命周期阶段,我们可以大致的将扩展接口和方法分为4类:
- 工厂后处理器接口方法
- Bean级生命周期接口
- 容器级生命周期接口
- Bean自身的方法
Bean自身的方法
- 如调用Bean构造方法实例化,调用Setter方法设置属性,以及通过
<bean>
的init-method , destroy-method所指定的方法
Bean级接口
- 如
BeanNameAware
、BeanFactoryAwar
、InitializingBean
和DisposableBean
等接口,这些接口都是由当前Bean自身实现的。即某个Bean想要获取BeanFactory,则该Bean则实现BeanFactoryAware 接口,重写setBeanFactory方法。因为是当前Bean实现的,它也仅对当前的Bean生效
容器级接口(重点理解)
- 不同于Bean级接口,容器级接口都不一样了,容器级接口,通常称为"后处理器"。后处理器接口一般不是由当前Bean去实现的,而是独立于Bean(虽然后处理器注册到容器中的形式,还是Bean),即不是由我们有需要的Bean去实现这些接口的,而是通常会找一个专门的Bean去实现这些接口,就像拦截器一样,说白了它们就是Spring提供的容器级Bean拦截器,但是他们不会拦截自己(即实现了后处理器的Bean是不会被后处理器处理的)
- 容器级接口有
InstantiationAwareBeanPostProcessor
和BeanPostProcessor
(当然BeanFactoryPostProcessor也是,但已经分类到工厂后处理器接口了) - 所有的容器级接口在整个Spring生命周期中可以有多个实现,Spring本身也提供了多个保证自己业务执行的实现。如果我们自定义了多个相同后处理器的接口实现,我们可以通过实现Ordered接口来保证这些后处理器的的处理(拦截)顺序
工厂后处理器接口
- 工厂后处理器接口指的就是
BeanFactoryPostProcessor
接口,在框架级别,Spring容器已经提供的AspectJWeavingEnabler、CustomAutowireConfigurer、ConfigurationClassPostProcessor等实现类。 - 说白了就是Spring提供的Bean实例化前的拦截器,可以在Spring Bean实例化前进行拦截,按需对某个Bean的元数据进行修改,赋值等操作
- 同时工厂后处理器也是容器级的生命周期接口,当有多个实现时,可以实现Ordered接口保证拦截顺序
Bean的生命周期
如何记住Bean生命周期的生命阶段?
为了方便记忆Bean生命周期已经常见的几个阶段
我将生命周期大致分为4个节点:
- 实例化
- 填充属性(属性注入)
- 自定义初始化
- 销毁
然后将Bean的生命周期扩展接口分布在4个节点前后,就形成为12个动作阶段
- Bean实例化元数据级前置操作 | BeanFactoryPostProcessor
- Bean实例化前置操作 | InstantiationAwareBeanPostProcessor
- Bean实例化 | Constructor
- Bean实例化后置操作 | InstantiationAwareBeanPostProcessor
- Bean属性注入前拦截操作 | InstantiationAwareBeanPostProcessor
- Bean属性注入,依赖注入 | DI
- Bean Aware | xxxAware
- Bean自定义初始化前置操作 | BeanPostProcessor
- Bean自定义初始化操作 | InitializingBean/init-method/@PostConstruct
- Bean自定义初始化后置操作 | BeanPostProcessor
- Bean准备完毕 | 使用
- Bean销毁前阶段 | DisposableBean/destory-method/@PreDestroy
所以我们知道,这里的12个生命周期动作阶段都是在4个生命周期节点前后插入的操作
当然实际的动作肯定不仅仅只有这一些,我们这里是只是指出主要的扩展接口。
Bean生命周期接口的分析
图来源于网上,这跟《精通Spring 4.x 企业应用开发实践》书中的图是一样的
我们这里就根据图中的流程重点讲解6个部分
- BeanFactoryPostProcessor
- InstantiationAwareBeanPostProcessor
- XXXAware
- BeanPostProcessor
- 自定义初始化逻辑
- 自定义销毁前逻辑
BeanFactoryPostProcessor
BeanFactoryPostProcessor可以说是Spring内置的Bean生命周期容器级拦截器,它的原理跟BeanPostProcessor基本相同,但并不是继承至BeanPostProcessor接口。它是也是一个顶级接口
实现它,我们只要重写一个方法postProcessBeanFactory()
, 这个方法的调用时机是在Bean实例化之前调用,是可以对Bean的元数据BeanDefinition进行修改操作的。即在Bean未实例化前就可以对Bean的元数据进行修改。
因为BeanFactoryPostProcessor是容器级的接口,所以通常我们需要一个类去实现它,重写postProcessBeanFactory()方法,根据方法传递的beanFactory去获取某个Bean的元数据,然后进行修改,但它是不会拦截自己本身的(因为通常BeanFactoryPostProcessor实现类本质也是一个Bean)
在Spring中的应用场景有:
![](https://img-blog.csdnimg.cn/20190109140929780.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NuYWlsTWFubg==,size_16,color_FFFFFF,t_70)
总结:
- 容器级接口
- 一个实现类只会执行一次postProcessBeanFactory()方法,按需修改
- 调用时机在实例化前,可以修改Bean元数据
- 多个实现类可以通过Order保证顺序
InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor也是一个容器级接口,虽然继承于BeanPostProcessor,但却是不竟相同的,InstantiationAwareBeanPostProcessor主要是在Bean实例化前后以及属性注入前执行的拦截操作
// postProcessBeforeInstantiation方法的作用在目标对象被实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例,该方法在Bean类加载前执行
// beanClass参数表示目标对象的类型,beanName是目标实例在Spring容器中的name
// 返回值类型是Object,如果返回的是非null对象,接下来除了postProcessAfterInitialization方法会被执行以外,其它bean构造的那些方法都不再执行。否则那些过程以及postProcessAfterInitialization方法都会执行
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
// postProcessAfterInstantiation方法的作用在目标对象被实例化之后并且在属性值被populate之前调用
// bean参数是目标实例(这个时候目标对象已经被实例化但是该实例的属性还没有被设置),beanName是目标实例在Spring容器中的name
// 返回值是boolean类型,如果返回true,目标实例内部的返回值会被populate,否则populate这个过程会被忽视
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
// postProcessPropertyValues方法的作用在属性中被设置到目标实例之前调用,可以修改属性的设置
// pvs参数表示参数属性值(从BeanDefinition中获取),pds代表参数的描述信息(比如参数名,类型等描述信息),bean参数是目标实例,beanName是目标实例在Spring容器中的name
// 返回值是PropertyValues,可以使用一个全新的PropertyValues代替原先的PropertyValues用来覆盖属性设置或者直接在参数pvs上修改。如果返回值是null,那么会忽略属性设置这个过程(所有属性不论使用什么注解,最后都是null)
PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException;
在Spring中的应用场景有:
![](https://img-blog.csdnimg.cn/2019010914095536.png)
要注意的点有:
- postProcessBeforeInstantiation方法是在Bean的类加载前执行的,所以可以看到方法传入的不是当前Bean
- postProcessPropertyValues方法时在Spring属性注入到Bean前执行的,但是在方法中对PropertyValues参数的修改会直接影响属性注入的结果,因为属性注入就是基于PropertyValues的值去注入的
- 如果postProcessAfterInstantiation返回false,则不会执行postProcessPropertyValues方法
小结:
- 容器级接口
- 同一个实现类的同一个方法,Spring有多少Bean,就会拦截多少次
- 调用时机在实例化前后以及属性注入前
- 多个实现类,可以通过Order保证顺序
BeanPostProcessor
BeanPostProcessor通常称为后处理器,是一个容器级接口,它的方法则主要是在Bean实例化和属性注入之后,自定义初始化前后执行的拦截操作,有两个方法:
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
before是自定义初始化前,after是自定义初始化后,同时我们可以看到传入的参数都是当前拦截的Bean实例对象,可以在方法中直接对它们进行修改操作。
BeanPostProcessor在Spring框架中占有重要的地位,为容器提供对Bean后续加工处理的切入点,Spring容器所提供的各种神奇的功能“AOP,动态代理”都是通过BeanPostProcessor实施的
![](https://img-blog.csdnimg.cn/20190109141601692.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NuYWlsTWFubg==,size_16,color_FFFFFF,t_70)
小结:
- 容器级接口
- 同一个实现类的同一个方法,Spring有多少Bean,就会拦截多少次
- 调用时间再实例化,属性注入,Aware之后;自定义初始化前后
- 多个实现类可以使用Order保证顺序
XXXAware
基本上Aware结尾的都是为Bean的生命周期中加上一个某个东西的引用,就像附装插件一样。Spring中 也提供了多种的Aware实现
![](https://img-blog.csdnimg.cn/20190109124146104.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NuYWlsTWFubg==,size_16,color_FFFFFF,t_70)
Aware是Bean级的生命周期接口,所以只有当有需要的Bean才会去实现他们,所以他们不会影响到其他没有实现该接口的Bean。
当我们实现了ApplicationContextAware接口,按我们就要实现setApplicationContext()方法
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
通过该方法,我们从参数中的到传入的上下文,并赋值给成员属性,那么我这个Bean就带有上下文的引用了。其他的Aware也类似。
小结:
- Bean级接口,按需实现
- 通常是为了让Bean持有某个东西的引用
- 调用时机是属性注入之后执行
自定义初始化逻辑
InitializingBean
InitializingBean是Bean级接口,作用是在Bean实例化且被填充了属性之后,Bean准备好之前的自定义初始化操作,只有一个方法要实现
void afterPropertiesSet() throws Exception;
The org.springframework.beans.factory.InitializingBean interface lets a bean perform initialization work after the container has set all necessary properties on the bean. The InitializingBean interfacespecifies a single method:
We recommend that you do not use the InitializingBean interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PostConstruct annotation or specifying a POJO initialization method. In the case of XML-based configuration metadata, you can use the init-method attribute to specify the name of the method that has a void no-argument signature. With Java configuration, you can use the initMethod attribute of @Bean.
对InitializingBean的解释,我们引用官方文档的解释:
- InitializingBean 是一个实例化回调(Initialization Callbacks),通过实现InitializingBean接口,我们可以在Spring容器为Bean填充了必要的属性之后,自定义一些初始化后逻辑。
- 但是官方不推荐我们在项目中使用InitializingBean 接口,而是使用@PostConstruct注解或者通过xml方式的
init-method
指定bean本身的某个初始化方法。因为使用了InitializingBean
接口,会让我们的代码跟Spring耦合在一起。
init-method属性
init-method属性指定的方式是Bean自身的方法,作用就是自定义初始化操作,作用类似于InitializingBean,但是比InitializingBean更晚执行
小贴士:
详细可以看文章下面的相关问题章节的init-method、@ProConstruct、@Bean(initMethod = “xxxMethod”)、initializingBean接口的区别
自定义销毁前逻辑
DisposableBean
DisposableBean是Bean级接口,作用是当容器关闭,触发Bean的销毁,则会销毁前执行的逻辑,只有一个方法要实现
void destroy() throws Exception;
Implementing the org.springframework.beans.factory.DisposableBean interface lets a bean get a callback when the container that contains
it is destroyed. The DisposableBean interface specifies a single
method:We recommend that you do not use the DisposableBean callback interface, because it unnecessarily couples the code to Spring.
Alternatively, we suggest using the @PreDestroy annotation or
specifying a generic method that is supported by bean definitions.
With XML-based configuration metadata, you can use the destroy-method
attribute on the . With Java configuration, you can use the
destroyMethod attribute of @Bean.
对于DisposableBean,我们也引用官方文档的解释:
- DisposableBean是一个销毁回调(Destruction Callbacks),实现该接口可以让容器关闭的时候,让该Bean获得一个方法回调去做一些事情
- 同样,我们也不推荐使用DisposableBean回调接口,而是使用
@PreDestroy
注解方式、xml方式的destroy-method
属性、Java Configuration方式的@Bean的destroyMethod
属性去代替。这样可以减少项目代码与Spring的耦合
destroy-method
destroy-method指定的方式是Bean自身的方法,作用是自定义的Bean销毁前操作,是Bean生命周期中最后一个执行的操作
小贴士:
详细可以看文章下面的相关问题章节的init-method、@ProConstruct、@Bean(initMethod = “xxxMethod”)、initializingBean接口的区别
Bean生命周期的代码实现分析
生命周期阶段的模拟
我们这里使用5个类来初步模拟一下Bean的生命周期的各个阶段:
前提声明:这里使用了lombok插件,强烈推荐使用。如果没有使用的小伙伴,可以把get/set方法加上,把log方法换成传统实现或者使用System.out.println输出
- Hello类
- HelloBeanPostProcessor类
- HelloBeanFactoryPostProcessor类
- HelloInstantiationAwareBeanPostProcessor类
- SpringBoot启动引导类
模拟了什么?
- 我们这里模拟了生命周期中各阶段的实现顺序,验证生命周期的流程
- 同时这里模拟了BeanFactoryPostProcessor工厂后处理器修改Bean元数据的操作
- 模拟了InstantiationAwareBeanPostProcessor是如何在bean注入属性阶段修改属性的操作
- 模拟了手动关闭Spring容器,触发Bean消耗的操作
模拟流程图
因为位置有限,只能省略了部分东西
代码模拟
hello类
@Slf4j
@Data
@Component
public class Hello implements InitializingBean, DisposableBean {
/**
* 静态代码块,类加载时输出
*/
static { log.warn("【static】 Hello class was loading"); }
/**
* 成员属性
*/
public String helloworld = "helloworld";
/**
* 无参构造函数
*/
public Hello() {
log.warn("【Constructor】 - initialization ");
}
/**
* 自定义Bean初始化方法
*/
@PostConstruct
public void postInit() {
log.warn("【PostConstruct】 - postInit");
}
/**
* 自定义Bean注销方法
*/
@PreDestroy
public void postDestroy(){
log.warn("【PreDestroy】 - postDestroy");
}
/**
* 实现InitializingBean的方法,自定义初始化逻辑
*
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
log.warn("【InitializingBean】 - afterPropertiesSet");
}
/**
* 实现DisposableBean的方法,自定义销毁逻辑
* @throws Exception
*/
@Override
public void destroy() throws Exception {
log.warn("【DisposableBean】 - destroy");
}
}
- 这是我们要进行模拟生命周期的Bean
- 实现了InitializingBean和DisposableBean 接口,提供了的初始化后和销毁前的自定义逻辑
- 使用了注解方式的@PosConstruct和@PreDestroy,用于对比InitializingBean和DisposableBean
- 实现了类加载时输出和构造实例时输出
HelloBeanFactoryPostProcessor
/**
* 工厂后处理器
*/
@Component
@Slf4j
public class HelloBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
/**
* 可以对bean实例化前,通过Ioc容器获取到bean的元数据,并对其元数据进行修改
* @param beanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//通过ioc容器,获取com.example.demo3.bean.Hello类的元数据
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("hello");
//从元数据中获得该Bean的成员参数(成员属性...)
MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();
//修改元数据中的属性
mutablePropertyValues.add("helloworld","HelloBeanFactoryPostProcessor - helloworld");
log.warn("【HelloBeanFactoryPostProcessor】 - postProcessBeanFactory");
}
}
- 这是自定义的工厂后处理器,只有一个postProcessBeanFactory方法。从流程图中,我们知道,它是第一个回调的地方
- 我们在postProcessBeanFactory方法中模拟修改Hello Bean的元数据信息,原本Hello类的helloworld属性的值是helloworld。我们把它的元数据记录的helloworld属性修改成HelloBeanFactoryPostProcessor - helloworld,看是否会影响该Bean的实例化
HelloInstantiationAwareBeanPostProcessor
@Slf4j
@Component
public class HelloInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
/**
* postProcessBeforeInstantiation方法的作用在目标对象被实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例
* 相比BeanFacotoryPostProcessor的方法,都是在实例化前调用,但是这里不能操作bean的元数据(BeanDefinition)
* 返回值类型是Object,如果返回值的是非null对象,那么接下来只有postProcessAfterInitialization方法会被执行
* 返回值如果是null,剩下的过程都会执行
*
* @param beanClass 目标对象的类型
* @param beanName 目标实例在Spring容器中的name
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if ("hello".equals(beanName)) {
log.warn("【HelloInstantiationAwareBeanPostProcessor】 - before");
return null;
}
return null;
}
/**
* postProcessAfterInstantiation方法的作用在目标对象被实例化之后并且在属性值被populate之前调用
*
* @param bean 拦截的bean实体
* @param beanName 目标bean的id
* @return
* @throws BeansException
*/
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if ("hello".equals(beanName)) {
log.warn("【HelloInstantiationAwareBeanPostProcessor】 - after");
return true;
}
return true;
}
/**
* postProcessPropertyValues方法的作用在属性中注入到目标实例之前调用,可以修改属性的设置
* 虽然是在注入属性前修改的属性,但最终的情况仍然是以本次修改为结果,即后面发生的注入行为是根据PropertyValues 属性进注入的
*
* @param pvs bean元数据中的MutablePropertyValues
* @param bean 当前实例化好的对象
* @param beanName
* @return
* @throws BeansException
*/
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if ("hello".equals(beanName)) {
//PropertyValues的修改才会呈现最后效果的
((MutablePropertyValues)pvs).addPropertyValue("helloworld","postProcessProperties - helloworld");
/* ((MutablePropertyValues)pvs).addPropertyValue("name",new Name("hhhh"));*/
//bean的修改是没有任何效果的
((Hello)bean).setHelloworld("helloworld");
log.warn("【HelloInstantiationAwareBeanPostProcessor】 - postProcessProperties");
return pvs;
}
return pvs;
}
}
- 这是HelloInstantiationAwareBeanPostProcessor 的实现类,它有三个方法
- 一个是在实例化前操作,另一个是在实例化后操作。还有一个是在填充属性前操作
- 这里只处理名字为hello的Bean,其他的Bean直接放行
HelloBeanPostProcessor
/**
* 后处理器
*/
@Component
@Slf4j
public class HelloBeanPostProcessor implements BeanPostProcessor {
/**
* 在bean实例化、属性注入之后,自定义初始化操作之前进行,可以对已经实例化好的bean进行修改
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("hello".equals(beanName)){
log.warn("【HelloBeanPostProcessor】 - before");
return bean;
}
return bean;
}
/**
* 在bean实例化、属性注入、自定义初始化操作之后进行,可以对自定义初始化后的bean进行修改
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("hello".equals(beanName)){
log.warn("【HelloBeanPostProcessor】 - after");
return bean;
}
return bean;
}
}
- 这是后处理器的实现类,它有两个方法
- 一个是在自定义初始化逻辑前执行,另一个实在自定义初始化后执行
- 这里只处理名字为hello的Bean,其他的Bean直接放行
SpringBoot启动引导类
@Slf4j
@SpringBootApplication
public class Demo3Application {
public static void main(String[] args) {
//获取Spring应用上下文
ApplicationContext applicationContext = SpringApplication.run(Demo3Application.class, args);
//获取Hello在Spring容器中的单例对象
Hello hello = applicationContext.getBean(Hello.class);
//输出修改后的hello对象
log.warn(hello.toString());
//关闭Spring容器,为了触发Bean的注销
((ConfigurableApplicationContext) applicationContext).close();
}
}
- 启动类中,我们会获取到Spring应用上下文,并通过上下文获取容器中的Hello bean,把他输出处理,查看变化
- 我们还启动类中通过上下文来关闭容器,模拟触发Bean销毁
结果
2019-01-09 11:32:12.660 【HelloBeanFactoryPostProcessor】 - postProcessBeanFactory
2019-01-09 11:32:13.019 【HelloInstantiationAwareBeanPostProcessor】 - before
2019-01-09 11:32:13.019 【static】 Hello class was loading
2019-01-09 11:32:13.019 【Constructor】 - initialization
2019-01-09 11:32:13.019 【HelloInstantiationAwareBeanPostProcessor】 - after
2019-01-09 11:32:13.019 【HelloInstantiationAwareBeanPostProcessor】 - postProcessProperties
2019-01-09 11:32:13.019 【HelloBeanPostProcessor】 - before
2019-01-09 11:32:13.019 【PostConstruct】 - postInit
2019-01-09 11:32:13.019 【InitializingBean】 - afterPropertiesSet
2019-01-09 11:32:13.019 【HelloBeanPostProcessor】 - after
2019-01-09 11:32:13.270 Hello(helloworld=postProcessProperties - helloworld)
2019-01-09 11:32:13.270 【PreDestroy】 - postDestroy
2019-01-09 11:32:13.270 【DisposableBean】 - destroy
从控制台输出的结果我们可以看到:
1. BeanFactoryPostProcessor 工厂后处理器最早执行,而且比第二执行的早了整整1.5s
2. InstantiationAwareBeanPostProcessor的前置操作第二执行,甚至比Bean的类加载还早
3. 类加载第三执行
4. 构造器第四执行
5. InstantiationAwareBeanPostProcessor的后置操作第五执行
6. InstantiationAwareBeanPostProcessor属性修改第六执行
7. Spring的属性注入第七执行(当然,从输出中,是看不到的)
8. BeanPostProcessor后处理器前置操作第八执行
9. @PostConstruct注解指定的初始化方法第九执行
10. InitializingBean的自定义初始化第十执行
11. BeanPostProcessor后处理器的后置操作第十一执行
12. 然后Bean进行准备就绪状态,我们将它输出,发现属性helloworld的确在工厂后处理器中被修改了,后面模拟了关闭容器
13. 容器关闭,首先触发@PreDestroy注解指定的方法,第十二执行
14. DisposableBean 的销毁前操作最后执行
所以我们能够得出重点结论:
- BeanFactoryPostProcessor 最早被执行,而且早非常多
- InstantiationAwareBeanPostProcessor的前置操作比类加载还早执行
- @PostConstruct初始化比InitializingBean方式更早执行
相关问题
init-method、@ProConstruct、@Bean(initMethod = “xxxMethod”)、initializingBean接口的区别
其实这是不仅仅是讨论init,也会说道destory回调。总是在Spring的看法中,本小结所说的方法都是Bean生命周期中发生在初始化和销毁阶段的一些回调方法,只不过,Spring可以支持多种形式的表现形式:
- 接口方式 | InitializingBean接口 ,DisposableBean 接口
- Xml方式 | init-method 属性,destroy-method属性
- Java 配置 | @Bean(initMethod = “” , destroyMethod= “”)
- 注解方式 | @PostConstruct、@PreDestroy
按Spring的说法,为了减少代码与Spring的耦合,官方是不推荐使用Spring提供的回调接口InitializingBean。而是推荐使用注解,Xml或者Java配置的方式去指定Bean的特定方法去实现
注解方式 | @PostConstruct、@PreDestroy
注解的方式的本质是通过后处理器(InitDestroyAnnotationBeanPostProcessor)去实现的
InitDestroyAnnotationBeanPostProcessor后处理器负责对标记了@PostConstruct、@PreDestroy注解的Bean进行拦截处理,在Bean初始化后和销毁前执行注解指定的相应逻辑
/**
* 自定义Bean初始化方法
*/
@PostConstruct
public void init() {
log.warn("hello PostConstruct init");
}
/**
* 自定义Bean注销方法
*/
@PreDestroy
public void destroy(){
log.warn("hello PreDestroy destroy");
}
Xml方式 | init-method 属性,destroy-method属性
Xml的方式目前一般发生在旧项目中,是曾经常见的使用方式,它可以在xml配置中指定某个Bean的初始化和销毁方法
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="destory"/>
Java 配置 | @Bean(initMethod = “” , destroyMethod= “”)
Java配置方式通常是用于通过@Bean方式注册到Spring容器中Bean中,它相比注解方式更加的类似Xml方式,可以说是Xml方式的替代品。
public class Hello{
public void beanInit(){
log.warn("hello Bean init");
}
public void beanDestory(){
log.warn("hello bean destory");
}
}
@Configuration
public class HelloConfig{
@Configuration
public class HelloConfig {
@Bean(initMethod = "beanInit",destroyMethod = "beanDestory")
public Hello hello(){
return new Hello();
}
}
如果它们同时存在,那么他们的执行顺序是怎么样的呢?
这里我还真测试了一下,但是没有测试Xml方式,因为嫌麻烦,同时根据下面官方文档的描述,我们也可以知道:
Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:
Methods annotated with @PostConstruct
afterPropertiesSet() as defined by the InitializingBean callback interface
A custom configured init() method
Destroy methods are called in the same order:
Methods annotated with @PreDestroy
destroy() as defined by the DisposableBean callback interface
A custom configured destroy() method
翻译如下:如多种生命周期机制实现在同一个Bean中,辣么顺序如下:
自定义初始化方法的顺序:
- @PostConstruct
- InitializingBean
- init方法(@Bean或Xml)
自定义的销毁方法的顺序:
- @PreDestory
- @DisposableBean
- destroy方法(@Bean或Xml)
BeanFactoryPostProcessor的方法和InstantiationAwareBeanPostProcessor的前置方法是否有重叠实现之处?
非也,BeanFactoryPostProcessor主要是对Bean的元数据进行操作;而InstantiationAwareBeanPostProcessor的前置方法虽然也是在实例化前操作,但是它并不修改元数据
BeanFactoryPostProcessor和BeanPostProcessor的区别?
BeanFactoryPostProcessor和BeanPostProcessor都是容器级接口,但是BeanFactoryPostProcessor本质不会去拦截所有的Bean,而是通过beanFactory按需获取想要更改的Bean元数据,然后进行修改。
- 即一个BeanFactoryPostProcessor只会执行一次,而一个BeanPostProcessor实现类则会执行很多次(Spring容器中有多少个Bean,它就会执行多少次),即BeanFactoryPostProcessor是与Bean无相关的,这是他们之间的一个很重要的区别
- 通常BeanFactoryPostProcessor是用于在实例化前修改Bean的元数据的(修改Bean的蓝图)。而BeanPostProcessor通常是对实例化后,获取Bean进行操作的,即为Bean的后续提供加工处理的切入点(当然不一定是实例化后,也可以实例化前,但不操作蓝图)
当Bean依赖其他Bean的时候,生命周期阶段又是怎么样执行的?
没有存在依赖,就会像上面所说的一样,按顺序执行每一个阶段。如果Bean之间有相互依赖呢?
我们假设一个场景:A依赖B
public class A{
@Autowired
public B b;
}
分两种情况,因为谁先加载的问题就是随机的:
- B先与A被虚拟机加载
- A先于B被虚拟机加载
B先与A被虚拟机加载
因为被依赖方先加载,所以就是个正常顺序流程,没什么好说的
A先于B被虚拟机加载
1. 【(A)InstantiationAwareBeanPostProcessor】 - before
2. 【(A)static 】 A class was loading
3. 【(A)Constructor】- initialization
4. 【(A)InstantiationAwareBeanPostProcessor】 - after
5. 【(A)InstantiationAwareBeanPostProcessor】 - postProcessProperties
6. A类属性注入触发B类的加载
7. 【(B)InstantiationAwareBeanPostProcessor】 - before
8. 【(B)static 】 B class was loading
9. 【(B)Constructor】- initialization
10. 【(B)InstantiationAwareBeanPostProcessor】 - after
11. 【(B)InstantiationAwareBeanPostProcessor】 - postProcessProperties
12. 【(B)BeanPostProcessor】 - before
13. 【(B)InitializingBean】 - afterPropertiesSet
14. 【(B)BeanPostProcessor】 - after
15. B类Bean进入就绪状态,A类Bean属性注入完成
16. 【(A)BeanPostProcessor】 - before
17. 【(A)InitializingBean】 - afterPropertiesSet
18. 【(A)BeanPostProcessor】 - after
19. A类Bean就绪
因为当A开始Bean的生命周期时,被依赖的B有可能还没有进入就绪状态,甚至还没有进行类的加载。如果此时A已经进入到属性注入阶段,那么这就会迫使触发B的类加载进程
Bean生命周期的属性注入阶段囊括了什么呢?什么是属性注入?
也许,你刚刚开始了解到Bean生命周期的属性注入阶段时(有的也称数据填充阶段),很容易就会联系到Spring的依赖注入,然后将它们联系起来。嗯 , maybe ~
所以本节的重要就是我们得了解这个属性注入阶段,到底做了什么?是否跟我们脑子里想的属性注入的意思一致呢?
属性注入阶段跟依赖注入有关系吗?
- 关系是有的,但该阶段不能囊括所有的依赖注入方式。
属性注入阶段做了什么?
- Bean实例化后,到达属性注入阶段,曾经Xml方式的手动注入的属性值在此阶段会被注入Bean中
- 自动依赖注入(@Autowired)的属性注入和设值注入的方式会在此阶段将所依赖的Bean注入
要注意的地方:
- 类的成员属性是在实例化时就已经初始化好了,所以在属性注入之前就已经完成属性初始化了,即构造方法的属性初始化已该阶段无关
- 自动依赖注入的构造注入的方式也与属性注入阶段无关,在属性注入之前的实例化阶段就会判断构造方法中是否有@Autowired注解,有的话就会调用特殊方法完成依赖注入。与属性注入阶段无关
参考资料
- 《精通Spring 4.x 企业应用开发实践》
- Spring官方文档
- Confused!! Bean’s lifecycle method chain - @answerer: axtavt | 这解决了我对BeanPostProcessor的定位
- BeanPostProcessor confusion - @answere: Piotr De | 这帮助我了解了BeanPostProcessor的应用场景
- Spring Bean Life Cycle
- Spring中Bean的生命周期是怎样的? - @作者:知乎 | 推荐答案:大闲人柴毛毛 和 徐滔
- Spring内部的BeanPostProcessor接口总结 - @作者:Format’s Notes | 里面介绍了更多的BeanPostProcessor子接口
- Spring Bean的生命周期(非常详细) - @作者:Chandler Qian | 生命周期流程代码实践