
本文系统解析 Spring 中 @PreDestroy、DisposableBean.destroy() 和 destroyMethod 三类销毁回调的触发时机、执行顺序及工程实践要点,帮助开发者在容器关闭时可靠释放数据库连接、线程池、文件句柄等底层资源。
本文系统解析 spring 中 `@predestroy`、`disposablebean.destroy()` 和 `destroymethod` 三类销毁回调的触发时机、执行顺序及工程实践要点,帮助开发者在容器关闭时可靠释放数据库连接、线程池、文件句柄等底层资源。
在 Spring 应用生命周期中,Bean 的安全销毁是保障系统稳定性与资源零泄漏的关键环节。尤其在微服务或长周期运行的应用中,若数据库连接未关闭、Netty Channel 未释放、定时任务未取消,极易引发内存溢出、端口占用或连接池耗尽等问题。Spring 提供了三种主流销毁回调机制:@PreDestroy 注解、DisposableBean 接口实现、以及 @Bean(destroyMethod = "...") 配置。它们并非随意并行执行,而是遵循严格定义的语义层级与调用顺序。
✅ 触发时机:不止于 context.close()
销毁回调仅在 Spring 容器进入关闭流程时触发,典型场景包括:
- 显式调用
ConfigurableApplicationContext.close()(如单元测试中); - JVM 接收到
SIGTERM(如 Kubernetes pod 终止、systemctl stop、Dockerstop); - Spring Boot 应用接收到
/actuator/shutdown端点请求(需启用); - 嵌入式 Web 容器(如 Tomcat)正常停机时,Spring 上下文随之关闭。
⚠️ 注意:Spring 并未直接使用 Runtime.getRuntime().addShutdownHook() 注册多个钩子——它只注册一个统一的 shutdown hook,其作用是调用 AbstractApplicationContext.doClose(),从而启动标准的上下文关闭流程。这意味着所有销毁逻辑均在 Spring 自身的生命周期管理框架内执行,具备事务性、可扩展性和可观测性,而非依赖 JVM 不可控的并发钩子调度。
✅ 执行顺序:语义明确、层级清晰
当容器关闭时,Spring 对单例(Singleton)Bean 按创建逆序逐个销毁(即最后创建的 Bean 最先销毁),而每个 Bean 内部的销毁回调则严格按以下固定三阶段顺序执行(见 Spring 官方文档):
-
@PreDestroy方法(最早执行)- 属于 JSR-250 标准,轻量级、声明式,适合记录日志、标记状态、发送监控事件;
- 若抛出异常,会被 Spring 静默捕获并记录 WARN 日志,不中断后续销毁流程;
- ❗ 不适用于强契约型资源释放(如
close()必须成功)。
-
DisposableBean.destroy()方法(中间执行)- Spring 原生接口,语义明确为“资源清理”,推荐用于关键资源释放;
- 异常同样被捕获记录,但因其位于资源持有层更近,逻辑更聚焦、可维护性更强;
- ✅ 最佳实践:HikariCP
DataSource、Apache HttpClientCloseableHttpClient、ScheduledExecutorService等应在此处显式.close()或.shutdown()。
-
自定义
destroyMethod(最后执行)- 通过
@Bean(destroyMethod = "shutdown")或 XMLdestroy-method配置; - 适合封装复杂终止逻辑、调用第三方 SDK 的 shutdown API,或兼容遗留代码;
- 方法必须无参、返回
void,且不应抛出受检异常(否则需包装为RuntimeException)。
- 通过
? 关键结论:三者顺序固定、不可覆盖、不并发——Spring 保证同一 Bean 的三个回调严格串行执行,且全局销毁顺序由容器统一协调,完全规避了
addShutdownHook的“未指定顺序”风险。
✅ 代码示例:三法共存的完整验证
@Component
public class ResourcefulService implements DisposableBean {
@PostConstruct
public void init() {
System.out.println("✅ ResourcefulService initialized");
}
@PreDestroy // 阶段1:最先执行
public void preDestroy() {
System.out.println("➡️ Stage 1: @PreDestroy — logging shutdown start");
}
@Override
public void destroy() throws Exception { // 阶段2:核心资源释放
System.out.println("➡️ Stage 2: DisposableBean.destroy() — closing DB connection...");
// dataSource.close(); // 生产中真实调用
}
public void customShutdown() { // 阶段3:最后执行
System.out.println("➡️ Stage 3: destroyMethod — notifying monitoring system");
// MetricsReporter.shutdown();
}
}配置类中启用 destroyMethod:
@Configuration
public class AppConfig {
@Bean(destroyMethod = "customShutdown")
public ResourcefulService service() {
return new ResourcefulService();
}
}运行 applicationContext.close() 后输出:
➡️ Stage 1: @PreDestroy — logging shutdown start ➡️ Stage 2: DisposableBean.destroy() — closing DB connection... ➡️ Stage 3: destroyMethod — notifying monitoring system
⚠️ 重要注意事项
-
Prototype Bean 不自动销毁:Spring 容器不管理 prototype Bean 的生命周期,其
@PreDestroy/DisposableBean/destroyMethod默认不会被调用,需手动跟踪并显式释放(如使用ObjectFactory或ApplicationContext.getBeanProvider()结合SmartInitializingSingleton)。 - 异常处理策略:三类销毁方法中任意一个抛出异常,Spring 均会记录日志但继续执行后续 Bean 的销毁流程,确保整体关闭不被单点故障阻塞。
-
Spring Boot Actuator 增强:启用
spring-boot-starter-actuator后,可通过POST /actuator/shutdown主动触发优雅关闭,并结合lifecycle.timeout-per-shutdown-phase=30s控制各阶段超时。 -
替代方案补充:对于需监听容器事件的场景,还可订阅
ContextClosedEvent(全局关闭完成)或ContextStoppedEvent(暂停状态),但其粒度粗于 Bean 级销毁回调。
掌握这三类销毁机制的触发时机与执行顺序,是构建高可靠性 Spring 应用的基石。优先使用 DisposableBean.destroy() 处理核心资源,辅以 @PreDestroy 做可观测性增强,再用 destroyMethod 封装外部系统协同逻辑——三层协作,让每一次关闭都从容不迫。

















