Spring 中 Bean 的生命周期
在Spring框架中,"bean"这一术语特指那些受Inversion of Control (IoC) 容器管理的对象,它们通常都通过Dependency Injection (DI) 自动装配,无需开发者直接干预其生命周期管理。然而,在某些场景下,对特定 bean 进行定制化的初始化与销毁操作成为必要,此时就可以通过 Spring 中的拓展接口来实现。
基本生命周期
Spring 中 IoC 的顶层接口是 BeanFactory
,默认实现是 DefaultListableBeanFactory
,我们可以在 BeanFactory 的注释文档中看到关于 bean 生命周期的介绍,其主要有以下几类:
初始化
以下初始化方法都发生在属性注入之后
-
调用各种实现了 Spring 中自带的 Aware 接口中的方法
-
执行实现了
BeanPostProcessor
接口的postProcessBeforeInitialization
方法 -
执行实现了
InitializingBean
接口的afterPropertiesSet
方法 -
调用 bean 的
init-method
-
执行实现了
BeanPostProcessor
接口的postProcessAfterInitialization
方法
销毁
-
执行实现了
DestructionAwareBeanPostProcessor
接口的postProcessBeforeDestruction
方法 -
执行实现了
DisposableBean
接口的destroy
方法 -
调用 bean 的
destroy-method
bean 整体的生命周期阶段可以分为:实例化、属性填充、初始化、running 和销毁,相关的拓展主要在初始化和销毁阶段
实现原理
Spring 会在 doCreateBean 过程中的 populateBean 方法之后执行 initializeBean 方法,该方法会按照 BeanFactory 文档中描述的顺序依次执行对应的生命周期钩子函数
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
拓展生命周期
在 Spring 中,除了 BeanFactory 中描述的钩子之外,还通过 CommonAnnotationBeanPostProcessor
提供了对 JSR-250 的 @PostConstruct
and @PreDestroy
的支持 (是基于 BeanPostProcessor
接口实现的)
顺序总览
因此,在 Spring 中的 createBean 阶段共提供了 4 对针对 bean 初始化和销毁的生命周期函数钩子,他们的执行顺序依次为:
-
BeanPostProcessor#postProcessBeforeInitialization
-
@PostConstruct
-
InitializingBean#afterPropertiesSet
-
init-method
-
BeanPostProcessor#postProcessBeforeDestruction
-
@PreDestroy
-
DisposableBean#destroy
-
destroy-method
区别
-
BeanPostProcessor 可以实现对已经实例化的 bean 进行替换,而其他钩子函数无法修改 bean 的引用对象
-
@PostConstruct、@PreDestroy 组合的钩子函数可以实现应用与 Spring 的解耦
-
init-method、destroy-method 相较于 InitializingBean、DisposableBean 的组合可以实现业务代码与 Spring 的解耦,但是不常用
应用级生命周期
以上所讲的生命周期钩子函数都是围绕单个 bean 的 createBean 阶段进行的,这个阶段通常在容器的单例创建锁中执行的。此阶段中的 bean 可能还未完全初始化,不应该在此阶段中调用外部 bean,因为存在初始化死锁或循环引用的风险。
@Configuration(proxyBeanMethods = false)
class CircularReference {
@Component
class C1(): BeanFactoryAware {
override fun setBeanFactory(beanFactory: BeanFactory) {
println(beanFactory.getBean(C1::class.java))
}
}
@Component
class C2(): BeanFactoryAware {
override fun setBeanFactory(beanFactory: BeanFactory) {
println(beanFactory.getBean(C1::class.java))
}
}
}
针对以上问题,可以通过应用级的生命周期钩子函数来解决,通常有以下几种方案:
-
实现
SmartInitializingSingleton
接口 -
实现
SmartLifecycle
接口 -
监听
ContextRefreshedEvent
事件
调用时机
-
SmartInitializingSingleton 对应的钩子函数会在 Spring 应用 refresh 阶段中所有单例 bean 初始化完成后触发,适合执行那些依赖整个容器状态的操作。
-
SmartLifecycle 对应的钩子函数会在 Spring 应用 refresh 阶段中的 finishRefresh 方法中调用。
-
ContextRefreshedEvent 事件也会在 Spring 应用 refresh 阶段中的 finishRefresh 方法中发布。
Lifecycle 和 SmartLifecycle 的区别是:后者的钩子函数会在 Spring 应用的 refresh 阶段被自动调用,而前者需要手动调用 application 的 start 方法
总结
Spring框架通过高度可定制的bean生命周期管理模型,赋予了开发者强大的控制能力,不仅能够精确地控制单个bean的行为,还能有效地管理整个应用的启动与关闭流程,是构建高质量、可维护的Java应用不可或缺的一部分。