一文搞懂 Spring Bean 的生命周期
一. 前言
在学习Spring框架的IOC、AOP两大功能之前,首先需要了解这两个技术的基础——Bean。在Spring框架中,Bean无处不在,IOC容器管理的对象就是各种各样的Bean。理解Bean的生命周期有助于我们更好的理解和使用Spring框架的IOC功能,也有助于我们理解框架如何初始化、使用和管理Bean。接下来我们通过代码实现观察 BeanFactory 与 ApplicationContext 中bean的生命周期。
二. BeanFactory中Bean的生命周期
Bean 的生命周期概括起来就是 4 个阶段:
- 实例化(Instantiation)
- 属性赋值(Populate)
- 初始化(Initialization)
- 销毁(Destruction)
在四个阶段中,Spring框架会向外暴露多个扩展点,此时业务代码可以根据情况,从不同的扩展点切入影响Bean的默认创建行为。
- 其中橙色和绿色的是容器级别生命周期接口,也就是所有的Bean初始化时都会发生作用。主要包含两个接口InstantiationAwareBeanPostProcessor、BeanPostProcessor,一般被称为类后处理器。也可根据BeanName进行过滤对指定的Bean进行后处理。
- 蓝色的是Bean级生命周期接口方法,只有实现了这些接口的Bean进行初始化时,才会起作用。包含BeanNameAware、BeanFactoryAware、InitializingBean、DisposableBean。
- 灰色的是Bean自身的方法,通过Bean定义构造函数、setter属性赋值函数、init-method 和 destroy-method 所指定的方法。
三.扩展点
3.1 InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor主要是 Bean 实例化前后的扩展点,通常用于修改特定目标 bean 的默认实例化行为,例如创建具有特殊 TargetSources 的代理(池化目标、延迟初始化目标等),或实现额外的注入策略,如字段注入。
该接口是一个特殊用途的接口,主要供框架内部使用,建议尽可能实现普通的 BeanPostProcessor 接口。
1 | public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { |
其中postProcessProperties
和postProcessPropertyValues
作用相似,且触发时机相同,只是在 5.1 版本之后,更建议使用postProcessProperties
。
3.2 Aware
Aware 接口为 Spring 容器的核心接口,是一个具有标识作用的超级接口,实现了该接口的 bean 是具有被 Spring 容器通知的能力,通知的方式是采用回调的方式。
Aware 接口是一个空接口,实际的方法签名由各个子接口来确定,且该接口通常只会有一个接收单参数的 set 方法,该 set 方法的命名方式为 set + 去掉接口名中的 Aware 后缀,即 XxxAware 接口,则方法定义为 setXxx(),例如 BeanNameAware(setBeanName),ApplicationContextAware(setApplicationContext)。
Aware 的子接口需要提供一个 setXxx
方法,我们知道 set 是设置属性值的方法,即 Aware 类接口的 setXxx
方法其实就是设置 xxx 属性值的。例如ApplicationContextAware
:
1 | public interface ApplicationContextAware extends Aware { |
详细介绍参考:IoC 之深入分析 Aware 接口
3.3 BeanPostProcessor
BeanPostProcessor
也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor
中定义的两个方法,接口如下:
1 | public interface BeanPostProcessor { |
详细实现原理参考:IoC 之深入分析 BeanPostProcessor
3.4 InitializingBean 和 init-method
InitializingBean 是一个接口,它为 Spring Bean 的初始化提供了一种方式,它有一个 #afterPropertiesSet()
方法,在 bean 的初始化进程中会判断当前 bean 是否实现了 InitializingBean,如果实现了则调用 #afterPropertiesSet()
方法,进行初始化工作。然后再检查是否也指定了 init-method
,如果指定了则通过反射机制调用指定的 init-method
方法。代码如下:
1 | public interface InitializingBean { |
详细介绍参考:IoC 之深入分析 InitializingBean 和 init-method
四. 源码分析
4.1 bean 实例化
在 #doCreateBean(...)
方法中,首先进行 bean 实例化工作,主要由 #createBeanInstance(...)
方法实现,该方法返回一个 BeanWrapper 对象。BeanWrapper 对象是 Spring 的一个低级 Bean 基础结构的核心接口,为什么说是低级呢?因为这个时候的 Bean 还不能够被我们使用,连最基本的属性都没有设置。而且在我们实际开发过程中,一般都不会直接使用该类,而是通过 BeanFactory 隐式使用。
BeanWrapper 接口有一个默认实现类 BeanWrapperImpl,其主要作用是对 Bean 进行“包裹”,然后对这个包裹的 bean 进行操作,比如后续注入 bean 属性。
在实例化 bean 过程中,Spring 采用“策略模式”来决定采用哪种方式来实例化 bean,一般有反射和 CGLIB 动态字节码两种方式。
InstantiationStrategy 定义了 Bean 实例化策略的抽象接口,其子类 SimpleInstantiationStrategy 提供了基于反射来实例化对象的功能,但是不支持方法注入方式的对象实例化。CglibSubclassingInstantiationStrategy 继承 SimpleInstantiationStrategy,他除了拥有父类以反射实例化对象的功能外,还提供了通过 CGLIB 的动态字节码的功能进而支持方法注入所需的对象实例化需求。默认情况下,Spring 采用 CglibSubclassingInstantiationStrategy。
关于 Bean 实例化的详细过程,请参考这篇文章:创建Bean的流程
4.2 激活Aware
当 Spring 完成 bean 对象实例化并且设置完相关属性和依赖后,则会开始 bean 的初始化进程( #initializeBean(...)
),初始化第一个阶段是检查当前 bean 对象是否实现了一系列以 Aware 结尾的的接口。
Aware 接口为 Spring 容器的核心接口,是一个具有标识作用的超级接口,实现了该接口的 bean 是具有被 Spring 容器通知的能力,通知的方式是采用回调的方式。
在初始化阶段主要是感知 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 。代码如下:
1 | // AbstractAutowireCapableBeanFactory.java |
- BeanNameAware:对该 bean 对象定义的 beanName 设置到当前对象实例中
- BeanClassLoaderAware:将当前 bean 对象相应的 ClassLoader 注入到当前对象实例中
- BeanFactoryAware:BeanFactory 容器会将自身注入到当前对象实例中,这样当前对象就会拥有一个 BeanFactory 容器的引用。
当然,Spring 不仅仅只是提供了上面三个 Aware 接口,而是一系列:
- LoadTimeWeaverAware:加载Spring Bean时织入第三方模块,如AspectJ
- BootstrapContextAware:资源适配器BootstrapContext,如JCA,CCI
- ResourceLoaderAware:底层访问资源的加载器
- PortletConfigAware:PortletConfig
- PortletContextAware:PortletContext
- ServletConfigAware:ServletConfig
- ServletContextAware:ServletContext
- MessageSourceAware:国际化
- ApplicationEventPublisherAware:应用事件
- NotificationPublisherAware:JMX通知
4.3 BeanPostProcessor
初始化第二个阶段则是 BeanPostProcessor 增强处理,在该阶段 BeanPostProcessor 会处理当前容器内所有符合条件的实例化后的 bean 对象。它主要是对 Spring 容器提供的 bean 实例对象进行有效的扩展,允许 Spring 在初始化 bean 阶段对其进行定制化修改,如处理标记接口或者为其提供代理实现。
4.4 InitializingBean 和 init-method
初始化阶段,会先执行InitializingBean接口的afterPropertiesSet方法,再执行自定义Init方法
1 | // AbstractAutowireCapableBeanFactory.java |
4.5 DisposableBean 和 destroy-method
与 InitializingBean 和 init-method
用于对象的自定义初始化工作相似,DisposableBean和 destroy-method
则用于对象的自定义销毁工作。
当一个 bean 对象经历了实例化、设置属性、初始化阶段,那么该 bean 对象就可以供容器使用了(调用的过程)。当完成调用后,如果是 singleton 类型的 bean ,则会看当前 bean 是否应实现了 DisposableBean 接口或者配置了 destroy-method
属性,如果是的话,则会为该实例注册一个用于对象销毁的回调方法,便于在这些 singleton 类型的 bean 对象销毁之前执行销毁逻辑。
但是,并不是对象完成调用后就会立刻执行销毁方法,因为这个时候 Spring 容器还处于运行阶段,只有当 Spring 容器关闭的时候才会去调用。但是, Spring 容器不会这么聪明会自动去调用这些销毁方法,而是需要我们主动去告知 Spring 容器。
- 对于 BeanFactory 容器而言,我们需要主动调用
#destroySingletons()
方法,通知 BeanFactory 容器去执行相应的销毁方法。 - 对于 ApplicationContext 容器而言,调用
#registerShutdownHook()
方法。
代码注释参考:Spring源码注释
五. 测试
下面用一个实例来真实看看看上面执行的逻辑,毕竟理论是不能缺少实践的:
1 | public class LifeCycleBean implements BeanNameAware,BeanFactoryAware,BeanClassLoaderAware,BeanPostProcessor, |
- LifeCycleBean 继承了
BeanNameAware
,BeanFactoryAware
,BeanClassLoaderAware
,BeanPostProcessor
,InitializingBean
,DisposableBean
六个接口,同时定义了一个test
属性用于验证属性注入和提供一个#display()
方法用于模拟调用。
配置如下:
1 | <bean id="lifeCycle" class="org.springframework.core.test.lifeCycleBean" |
- 配置
init-method
和destroy-method
。
测试方法如下:
1 | // BeanFactory 容器一定要调用该方法进行 BeanPostProcessor 注册 |
运行结果:
1 | 构造函数调用... |
本文参考至: