如何让Spring Boot应用在Windows关机、重启、注销时接收关闭事件?
我懂你的痛点——context.registerShutdownHook()只能捕获手动Ctrl+C的关闭信号,完全拿Windows系统层面的关机、重启、注销操作没办法。下面给你两个实战方案,尤其是把应用部署成Windows服务的方式,这是目前最可靠的解决办法。
方案一:将Spring Boot应用部署为Windows服务(强烈推荐)
这是Windows平台下处理系统级关闭事件的标准姿势,因为Windows服务会在系统关机、重启或注销时收到系统发送的停止指令,Spring Boot可以通过自身的生命周期钩子完美响应这个信号。
步骤1:打包可执行JAR包
先确保你的Spring Boot应用已经打包成可执行JAR——用Maven的话执行mvn package,用Gradle就跑gradle bootJar,生成的JAR包直接就能运行。
步骤2:用WinSW把JAR包装成Windows服务
WinSW是个轻量工具,专门用来把任意可执行程序转成Windows服务,操作很简单:
- 下载WinSW的对应版本(比如64位系统下的
WinSW-x64.exe),把它重命名成和你的应用相关的名字,比如my-spring-boot-app.exe。 - 在同一个目录下创建一个同名的XML配置文件(比如
my-spring-boot-app.xml),配置内容参考下面的示例,记得替换成你自己的路径和参数:
<service> <id>MySpringBootService</id> <name>我的Spring Boot应用服务</name> <description>把Spring Boot应用作为Windows系统服务运行</description> <executable>java</executable> <arguments>-jar "D:\projects\my-app\target\my-app-1.0.0.jar"</arguments> <logpath>D:\projects\my-app\logs</logpath> <logmode>rotate</logmode> <!-- 日志自动轮转 --> <onfailure>restart</onfailure> <!-- 服务崩溃时自动重启 --> </service>
- 以管理员身份打开命令提示符,进入到这个目录,执行安装命令:
my-spring-boot-app.exe install - 启动服务:
my-spring-boot-app.exe start
步骤3:在Spring Boot中编写关闭逻辑
你可以通过实现DisposableBean接口,或者用@PreDestroy注解来定义关闭时要执行的操作,当Windows服务收到停止信号时,Spring会自动触发这些方法:
实现接口的方式:
@Component public class AppShutdownHandler implements DisposableBean { @Override public void destroy() throws Exception { // 这里写你的关闭逻辑:比如释放数据库连接、保存缓存数据、发送通知等 System.out.println("收到Windows系统停止信号,正在执行应用清理操作..."); } }
或者用更简洁的注解方式:
@Component public class AppShutdownHandler { @PreDestroy public void onAppShutdown() { // 自定义关闭逻辑 System.out.println("应用即将关闭,开始执行收尾任务..."); } }
当Windows执行关机、重启或注销操作时,系统会先给所有运行的服务发送停止指令,你的Spring Boot应用就能稳稳捕获到这个信号,执行你定义的清理逻辑。
方案二:用JVM Shutdown Hook临时处理(仅适合测试)
如果只是临时测试,不想折腾服务部署,可以试试手动添加JVM级别的Shutdown Hook,但这个方案可靠性不如服务——比如Windows强制关机时,可能不会等JVM执行完钩子逻辑就直接结束进程。
在Spring Boot主类里添加这段代码:
@SpringBootApplication public class MyApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args); // 添加JVM shutdown hook Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("捕获到系统关闭信号,开始关闭应用上下文..."); context.close(); // 这里可以加更多自定义清理逻辑 })); } }
不过要注意,当应用作为普通控制台程序运行时,Windows注销或关机时可能会弹出提示框,问要不要强制关闭程序,如果用户点了立即关闭,钩子逻辑可能来不及执行。所以这个方案只适合临时验证,生产环境一定要用服务方式。
验证方式
部署成服务后,直接重启Windows系统,然后去应用的日志目录里看日志,确认destroy()或者@PreDestroy方法里的内容有没有被执行,就知道是否生效了。
内容的提问来源于stack exchange,提问作者Irene

