Spring Boot 2.7 中资源销毁的先后顺序
在 Spring Boot 2.7 应用中,当应用上下文关闭时(例如,由于 JVM 关闭或调用),资源销毁的严格先后顺序发布关闭事件首先发布。允许应用程序监听器 () 最先感知到关闭事件并做出响应。停止 Lifecycle 组件调用所有LifecycleBean 的stop()方法。这是停止嵌入式 Web 服务器(如 Tomcat、Netty)的关键时刻。服务器停止接收新请求,并开始优雅关闭现有连
详细解析 AbstractApplicationContext 类中的 doClose() 方法,并总结在 Spring Boot 2.7 中资源销毁的先后顺序。
方法详解
doClose() 方法是 Spring 应用上下文关闭过程的核心方法。它被设计为 protected,意味着它主要供 Spring 框架内部调用(例如,由 close() 方法调用),但子类也可以重写它(通常通过 onClose() 钩子方法)以添加自定义的关闭逻辑。
方法执行流程如下:
-
状态检查与原子性关闭确认
if (this.active.get() && this.closed.compareAndSet(false, true)) {this.active.get(): 检查上下文当前是否处于活动状态。只有在活动状态下才需要执行关闭操作。this.closed.compareAndSet(false, true): 这是一个原子操作 (CAS),它将closed状态从false设置为true。这个操作是线程安全的关键,它确保多个线程同时调用close()时,只有其中一个线程能成功进入关闭流程,后续的调用将直接返回,避免重复关闭。
-
日志记录
if (logger.isDebugEnabled()) { logger.debug("Closing " + this); }- 如果启用了 Debug 日志,则记录一条上下文正在关闭的日志。
-
LiveBeansView 注销 (非原生镜像环境)
if (!NativeDetector.inNativeImage()) { LiveBeansView.unregisterApplicationContext(this); }LiveBeansView是 Spring 的一个工具类,用于提供当前应用中所有活动 Bean 的实时视图(常用于 IDE 工具集成)。NativeDetector.inNativeImage()用于判断应用是否运行在 GraalVM 原生镜像中。在原生镜像中,此功能不被支持,因此跳过注销。
-
发布 ContextClosedEvent 事件
try { publishEvent(new ContextClosedEvent(this)); } catch (Throwable ex) { logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex); }- 重要步骤:发布一个
ContextClosedEvent事件。 - 所有实现了
ApplicationListener<ContextClosedEvent>接口的 Bean 都会收到这个通知。 - 这为其他组件在容器正式销毁前执行一些清理操作提供了机会(例如,关闭线程池、断开网络连接、持久化未保存的数据等)。
- 异常被捕获并记录为警告,不会中断关闭流程。
- 重要步骤:发布一个
-
停止 Lifecycle 组件
if (this.lifecycleProcessor != null) { try { this.lifecycleProcessor.onClose(); } catch (Throwable ex) { logger.warn("Exception thrown from LifecycleProcessor on context close", ex); } }- 重要步骤:获取
LifecycleProcessor并调用其onClose()方法。 LifecycleProcessor会通知所有实现了Lifecycle接口的 Bean(例如,Tomcat 嵌入式服务器、Netty 服务器等),让它们执行标准的停止流程。- 这一步是为了避免在后续单个 Bean 销毁过程中出现延迟,优先停止那些需要优雅关闭的组件(如 Web 服务器)。
- 异常同样被捕获并记录,不会中断流程。
- 重要步骤:获取
-
销毁所有单例 Bean
destroyBeans();- 核心步骤:调用
DefaultListableBeanFactory的destroySingletons()方法。 - 这是资源销毁的主体部分。它会:
- 遍历所有已创建的单例 Bean。
- 如果 Bean 实现了
DisposableBean接口,则调用其destroy()方法。 - 如果 Bean 在定义时指定了自定义的销毁方法(如
@Bean(destroyMethod="...")或 XML 中的destroy-method),则调用该方法。
- 常见的清理工作在此发生,如:数据库连接池的关闭 (
DataSource)、Hibernate SessionFactory 的关闭、缓存管理器的关闭等。
- 核心步骤:调用
-
关闭底层 BeanFactory
closeBeanFactory();- 该方法通常将
this.beanFactory设置为null,表示这个 BeanFactory 已不可用。 - 对于可刷新的应用上下文(如
GenericApplicationContext),它还会重置序列化 ID,为下一次refresh()做准备。
- 该方法通常将
-
调用子类钩子方法
onClose();- 这是一个空的模板方法 (
protected void onClose() {}),专为子类设计。 - 子类(如
ServletWebServerApplicationContext)可以重写此方法,在上下文完全关闭之前添加一些特定的清理逻辑。
- 这是一个空的模板方法 (
-
重置应用监听器列表
if (this.earlyApplicationListeners != null) { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); }- 将
applicationListeners列表重置回最初在refresh()之前的状态(即earlyApplicationListeners)。 - 这确保了如果上下文之后被再次刷新(
refresh()),监听器列表处于一个干净、正确的初始状态。
- 将
-
状态置为未激活
this.active.set(false);- 最后,将
active状态标志设置为false。此时,整个上下文的关闭流程正式完成。
- 最后,将
总结:Spring Boot 2.7 资源销毁执行顺序
在 Spring Boot 2.7 应用中,当应用上下文关闭时(例如,由于 JVM 关闭或调用 SpringApplication.exit()),资源销毁的严格先后顺序如下:
-
发布关闭事件
- 首先发布
ContextClosedEvent。允许应用程序监听器 (ApplicationListener) 最先感知到关闭事件并做出响应。
- 首先发布
-
停止 Lifecycle 组件
- 调用所有
LifecycleBean 的stop()方法。这是停止嵌入式 Web 服务器(如 Tomcat、Netty)的关键时刻。服务器停止接收新请求,并开始优雅关闭现有连接。
- 调用所有
-
销毁单例 Beans
- 按依赖关系反向销毁所有单例 Bean(依赖方先于被依赖方销毁)。
- 执行每个 Bean 的销毁方法:
@PreDestroy注解的方法。DisposableBean接口的destroy()方法。@Bean(destroyMethod="...")中指定的自定义方法(例如,DataSource的close方法通常在这里被调用以关闭连接池)。
-
框架级清理
- 执行特定于上下文类型的最终清理(通过
onClose()钩子)。 - 重置内部状态(如监听器列表)。
- 执行特定于上下文类型的最终清理(通过
核心要点:
- 事件驱动:自定义清理逻辑应优先考虑实现
ApplicationListener<ContextClosedEvent>,以便最早执行。 - 优雅关闭:Web 服务器等需要优雅关闭的组件通过在
Lifecycle阶段停止。 - 资源释放:数据库连接、线程池等资源的释放主要在 Bean 销毁阶段通过各自的
destroy方法完成。 - 顺序保障:这个顺序确保了依赖项(如数据库连接池)在其使用者(如 DAO Bean)之后才被销毁,并且像 Web 服务器这样的基础设施最早开始停止,最大限度地保证了关闭过程的平稳和无残留。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)