项目概况

JVM钩子hooks函数

2026-01-292 min read后端技术
Java进阶

作者: 西魏陶渊明 博客: https://blog.springlearn.cn/

!!! tip 西魏陶渊明 莫笑少年江湖梦,谁不少年梦江湖 !!!

什么是钩子函数,在学习钩子函数之前,小编先提一个问题。

请问在Spring中,如果JVM异常终止,Spring是如何保证会释放掉占用的资源,比如说数据库连接等资源呢?

钩子函数非常简单,简单到小编只用摘抄一段Spring代码即可。走你,现在开始。

问题

Spring 容器中 Bean 在什么时候执行销毁方法?

我们知道在Spring中定义销毁方法有两种方式

  1. 实现 DisposableBeandestroy 方法。
  2. 使用 @PreDestroy 注解修饰方法
text
1@Component 2public class DataCollectBean implements DisposableBean { 3 4 /** 5 * 第一种方法实现 DisposableBean#destroy方法 6 * 7 * @throws Exception 异常 8 */ 9 @Override 10 public void destroy() throws Exception { 11 System.err.println("执行销毁方法"); 12 } 13 14 /** 15 * 第二种方法使用PreDestroy注解声明销毁方法 16 */ 17 @PreDestroy 18 public void customerDestroy() { 19 System.err.println("执行自定义销毁方法"); 20 } 21 22 23} 24

那么在什么时候执行销毁方法?

  1. 主动执行销毁bean
text
1 public static void main(String[] args) { 2 ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); 3 DataCollectBean bean = run.getBean(DataCollectBean.class); 4 //1. 主动销毁bean 5 run.getBeanFactory().destroyBean(bean); 6 }
  1. JVM关闭时候自动执行销毁方法。

这里就要用到钩子函数了, Spring 的钩子函数在 AbstractApplicationContext#shutdownHook属性

如果我们是SpringBoot项目我们看到在SpringApplication启动时候会注册一个钩子函数

如何定义钩子函数?

简直太简单了,没有任何学习成本。一行代码就能搞定。

text
1public class HooksTester { 2 public static void main(String[] args) { 3 Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 4 @Override 5 public void run() { 6 System.out.println("钩子函数执行"); 7 } 8 })); 9 //当主动关闭应用 10 while (true); 11 } 12}

触发钩子函数的场景

只要不是机器断电,强制kill -9 强制杀进程,都会触发。

钩子函数能做什么?

正如上图所示优雅停机,在项目将要关闭时候,主动释放程序占用的资源信息,释放db连接池的连接等其他占用的资源信息。 如果我们是 Spring 项目其实我们不用自己定义钩子函数,我们只要使用Spring提供给我们的销毁方法即可。因为 Spring定义的钩子函数中会去执行, DisposableBean.destory() 和被 PreDestroy 修饰的方法。

我们看下源码

text
1protected void doClose() { 2 // Check whether an actual close attempt is necessary... 3 if (this.active.get() && this.closed.compareAndSet(false, true)) { 4 if (logger.isDebugEnabled()) { 5 logger.debug("Closing " + this); 6 } 7 8 LiveBeansView.unregisterApplicationContext(this); 9 10 try { 11 // Publish shutdown event. 12 publishEvent(new ContextClosedEvent(this)); 13 } 14 catch (Throwable ex) { 15 logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex); 16 } 17 18 // Stop all Lifecycle beans, to avoid delays during individual destruction. 19 if (this.lifecycleProcessor != null) { 20 try { 21 this.lifecycleProcessor.onClose(); 22 } 23 catch (Throwable ex) { 24 logger.warn("Exception thrown from LifecycleProcessor on context close", ex); 25 } 26 } 27 28 // Destroy all cached singletons in the context's BeanFactory. 29 destroyBeans(); 30 31 // Close the state of this context itself. 32 closeBeanFactory(); 33 34 // Let subclasses do some final clean-up if they wish... 35 onClose(); 36 37 // Reset local application listeners to pre-refresh state. 38 if (this.earlyApplicationListeners != null) { 39 this.applicationListeners.clear(); 40 this.applicationListeners.addAll(this.earlyApplicationListeners); 41 } 42 43 // Switch to inactive. 44 this.active.set(false); 45 } 46 }

可以看到:doClose()方法会执行bean的destroy(),也会执行SmartLifeCycle的stop()方法,我们就可以通过重写这些方法来实现对象的关闭,生命周期的管理,实现平滑shutdown

最后求关注,求订阅,谢谢你的阅读!