Spring 源码分析——Bean 初始化和销毁
本篇文章讨论 Spring 源码中的初始化和销毁操作,以及什么时候触发初始化和销毁函数。
对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/08-bean-init-destroy
前言
在之前我们有讲了 Bean 的实例化、初始化过程,以及在 Spring 上下文 Spring 上下文 ApplicationContext 中,Spring 是如何实现提前加载 Bean 的,这其中包含了有些 Bean 是被提前加载的(例如 Spring 容器本身需要的 Bean)、有些 Bean 是被延迟加载只有在注入时才会被初始化(例如业务中用@Component 或@Service 标记的 Bean)。
但是既然 Bean 的生命周期中存在实例化、初始化过程,就必然包含生命周期末尾的销毁过程,在之前的文章中我们知道 Spring 获取一个单例 Bean 的接口是SingletonBeanRegistry
,而此接口的默认实现类是DefaultSingletonBeanRegistry
,在这个实现类中包含了三级缓存处理逻辑。如果你仔细看能发现其中也包含了destroyXxx
相关函数。
例如 DefaultSingletonBeanRegistry 中的destroySingletons
函数:
public void destroySingletons() {
if (logger.isTraceEnabled()) {
logger.trace("Destroying singletons in " + this);
}
synchronized (this.singletonObjects) {
this.singletonsCurrentlyInDestruction = true;
}
String[] disposableBeanNames;
synchronized (this.disposableBeans) {
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
}
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
destroySingleton(disposableBeanNames[i]);
}
this.containedBeanMap.clear();
this.dependentBeanMap.clear();
this.dependenciesForBeanMap.clear();
clearSingletonCache();
}
这是在 Spring 内部维护一个 Bean 的生命周期,在特定的时候由 Spring 内部去销毁 Bean 而调用的函数。
InitializingBean、DisposableBean
我们知道,在容器启动时 Spring 上下文 ApplicationContext 会预先通过refresh
函数提前装配 Bean,这其中包含了 Spring 内部主动调用 BeanFactory#getBean()
函数装配 Bean,并且在DefaultSingletonBeanRegistry
内部提供了销毁 Bean 的函数。
但是这里要提到的是,Spring 提供了两个外部接口用于初始化和销毁 Bean,实现这两个接口的类将会在初始化时或者销毁时被 Spring 调用;注意:这两个接口并不是必须实现的,并且它是 Spring 给外部暴露的两个管理 Bean 生命周期的接口,是否实现取决于业务本身,如果项目中没有主动实现这两个接口(或者使用一些注解),那么 Spring 将不会执行这两个接口中的函数,而是在 Spring 容器内部去管理 Bean 的初始化和销毁操作。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
public interface DisposableBean {
void destroy() throws Exception;
}
-
InitializingBean 接口: 当一个 Bean 实现此接口,Spring 容器会在 bean 的属性设置完成后调用 afterPropertiesSet()
方法;通常用于执行某些初始化操作。 -
DisposableBean 接口: 当一个 Bean 实现此接口,Spring 会在 bean 销毁时调用 destroy()
方法;通常用于执行某些清理操作。
如何使用?
-
直接实现这两个接口并标记为 bean
-
使用注解:@PostConstruct 标记方法,在 bean 创建后立即执行用于初始化操作;@PreDestroy 标记方法,在 bean 被销毁之前执行用于释放资源操作;
public class MyBean {
@PostConstruct
public void init() {
// 初始化逻辑
}
@PreDestroy
public void cleanup() {
// 清理逻辑
}
}
-
使用 XML 配置的
init-method
和destroy-method
<bean id="myBean" class="com.example.MyBean" init-method="initMethod" destroy-method="destroyMethod">
<!-- 设置 bean 的属性 -->
</bean>
close
在之前提到 Spring 上下文 ConfigurableApplicationContext 中存在一个refresh
函数用于在容器初始化时调用,同样还存在一个close
函数在容器关闭时调用:

close
接口实现在AbstractApplicationContext
:
@Override
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
// If we registered a JVM shutdown hook, we don't need it anymore now:
// We've already explicitly closed the context.
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}
catch (IllegalStateException ex) {
// ignore - VM is already shutting down
}
}
}
}
protected void doClose() {
......
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// Close the state of this context itself.
closeBeanFactory();
// Let subclasses do some final clean-up if they wish...
onClose();
......
}
protected void destroyBeans() {
getBeanFactory().destroySingletons();
}
可以看到,其实 Spring 内部在容器关闭时会通过执行close
函数进而调用DefaultSingletonBeanRegistry#destroySingletons()
统一销毁单例 Bean 释放资源。
Spring 容器默认在关闭容器是销毁所有 bean 实例,其中包含几个过程:
-
关闭 ApplicationContext:Spring 容器收到关闭信号后,会先关闭 ApplicationContext 上下文本身; -
销毁单例 bean:Spring 容器会依次销毁所有单例 bean 实例(包括使用@Component、@Service、@Repository 等注解定义的 Bean),并清理相关资源; -
销毁其他 bean:Spring 容器会依次销毁所有非单例 bean 实例,也就是每次请求都创建一个新实例的 prototype bean;
Spring 源码专栏
此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题。
配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs
联系我
-
个人博客:http://tycoding.cn/ -
GitHub:https://github.com/tycoding -
微信公众号:程序员涂陌 -
QQ 交流群:866685601
原文始发于微信公众号(程序员涂陌):Spring 源码分析——Bean初始化和销毁
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145587.html