SmartLifecycle 的 stop() 方法默认不执行,需同时满足容器支持优雅停机、组件处于运行状态且 Spring 上下文参与 shutdown;常见失效原因是 start() 中未设置 isRunning() 为 true;流量拦截组件应设较小 getPhase() 值(如-1000)优先停止,业务资源组件设较大值(如 Integer.MAX_VALUE)延后停止,严禁用 @Order 或 @DependsOn 控制顺序。

SmartLifecycle 的 stop() 方法在应用关闭时默认不执行,除非你同时满足三个条件:容器必须支持优雅停机、组件必须处于运行状态、且 Spring 上下文需完整参与 shutdown 流程。
为什么 stop() 方法经常“静默失效”
最常见的现象是:应用收到 SIGTERM 后,start() 执行了,但 stop() 完全没被调用。这不是 Bug,而是 Spring 的契约设计使然:
-
stop()只有在isRunning()返回true时才会被触发 —— 如果你在start()里忘了把内部状态设为 true,stop()就永远不会进; - Spring Boot ApplicationContext.close() 被跳过,整个生命周期链(包括
SmartLifecycle.stop())直接被绕过; - 如果应用被
kill -9强杀,JVM 进程立即终止,任何 Java 层回调(包括stop())都来不及执行。
必须配置的三项基础项
缺一不可,否则 stop() 就是摆设:
- 启用 Spring Boot 内置优雅停机:
server.shutdown=graceful(2.3+),或手动注册GracefulShutdown实现(2.3 之前); - 确保 Web 容器(如 Tomcat)配合:添加配置
spring.lifecycle.timeout-per-shutdown-phase=30s,避免超时强制中断; - 在
SmartLifecycle实现中,用线程安全方式维护运行态,例如private final AtomicBoolean running = new AtomicBoolean(false);,并在start()和stop()中严格使用compareAndSet()更新状态。
stop(Runnable callback) 的真实用途
这个重载方法不是为了“多一个选择”,而是为了解耦资源释放与容器关闭的同步等待:
- 当你在
stop()里执行耗时操作(如等待正在处理的 MQ 消息完成),不能阻塞主线程 —— 否则 Web 容器无法及时进入“拒绝新请求”阶段; - 正确做法是:在
stop(Runnable callback)中启动异步清理,并在清理完成后主动调用callback.run(); - Spring 会等待该
callback执行完毕,才继续执行后续 Bean 销毁流程; - 示例片段:
@Override public void stop(Runnable callback) { if (running.compareAndSet(true, false)) { CompletableFuture.runAsync(() -> { // 等待活跃任务完成、断开连接、清空缓冲区... resource.shutdownGracefully(); callback.run(); // ⚠️ 必须调用,否则停机卡住 }); } }
流量切除必须早于资源回收
顺序错了就等于没做优雅停机。SmartLifecycle 的 getPhase() 是唯一可控手段:
- 让流量拦截组件(如自定义 Filter、Actuator shutdown endpoint 的前置钩子)用较小的
getPhase()值(如-1000),确保它最先stop(),立刻停止接收新请求; - 让业务资源组件(DB 连接池、MQ 消费者)用较大的
getPhase()值(如Integer.MAX_VALUE),确保它们最后stop(),有足够时间处理完存量请求; - 切记:不要依赖
@Order或@DependsOn控制 SmartLifecycle 的执行顺序 —— 它们不生效。
真正难的不是写 stop(),而是在 JVM 进程即将退出的几十毫秒窗口里,协调 Web 容器拒绝新连接、Spring 容器允许旧请求走完、业务代码完成资源释放 —— SmartLifecycle 提供的是钩子,但每一步的时机、状态和超时,都得你自己掐准。










