Spring registerShutdownHook()不生效但close()正常问题排查
问题复现
开发Spring应用时,以下代码可正常执行,能够正确销毁Spring容器中已创建的Bean:
ApplicationContext context = SpringApplication.run(DemoApplication.class, args); ((ConfigurableApplicationContext) context).close();
替换为如下逻辑后,程序执行后既不会销毁Bean,也不会正常关闭应用,即使将context的声明类型从ApplicationContext直接改为AbstractApplicationContext,该异常仍然存在:
ApplicationContext context = SpringApplication.run(DemoApplication.class, args); ((AbstractApplicationContext) context).registerShutdownHook();
核心原因
问题根源是对registerShutdownHook()的作用存在理解偏差,两个方法的核心逻辑完全不同:
close()是主动同步触发容器关闭:调用后会立刻执行所有Bean的销毁回调、释放容器持有的资源,执行完成后容器直接进入关闭状态,和JVM是否退出没有关系。registerShutdownHook()本身不会触发任何关闭逻辑,也不会阻塞程序执行:它的作用只是提前给JVM注册一个回调标记,只有当JVM收到正常终止信号(程序自然运行结束、收到SIGTERM信号、系统正常关机)时,才会执行这个回调,进而调用容器的关闭流程。
出现不销毁Bean、不关闭应用的表现完全符合方法逻辑:
- 如果是Web类型的SpringBoot应用:
SpringApplication.run()启动完成后会拉起嵌入式Web服务器(Tomcat/Netty等)阻塞主线程持续监听请求,执行完registerShutdownHook()后程序会一直处于运行状态对外提供服务,根本不会触发JVM退出,自然不会执行关闭钩子,Bean不会销毁、应用也不会停止。 - 如果是非Web应用:执行完
registerShutdownHook()后如果主线程直接运行结束,JVM会正常退出,但如果是通过IDE强制终止进程、执行kill -9这类强制杀进程的操作,JVM不会执行任何注册的关闭钩子,自然也不会销毁Bean。 - 强转成
AbstractApplicationContext还是ConfigurableApplicationContext没有区别,两个类型里的registerShutdownHook()是同一个实现,修改声明类型不会改变方法执行逻辑。
额外说明:SpringBoot默认已经开启了自动注册关闭钩子的配置,对应配置项是spring.main.register-shutdown-hook=true,正常启动流程中框架已经自动完成了钩子注册,不需要手动重复调用。
正确使用方式
- 需要立刻主动关闭容器时,直接调用
close()方法即可,和第一段示例的写法一致,会同步执行完所有销毁逻辑再返回。 - 需要让程序在后续正常退出时自动销毁Bean,才需要用到
registerShutdownHook():- 非Web应用场景下,注册完钩子后正常执行业务逻辑,业务流程跑完JVM正常退出时会自动触发容器销毁
- Web应用场景下,注册完钩子后程序会正常对外服务,只有收到正常停止信号时才会触发销毁流程,不会在调用方法时直接关停应用
- 排查钩子不生效的问题时,优先确认进程是不是被强制终止的,强制终止场景下所有关闭钩子都不会执行,属于JVM的固有机制,和Spring逻辑无关。
内容的提问来源于stack exchange,提问作者Toji
相关产品推荐
相关产品推荐

