什麼是鉤子函數,在學習鉤子函數之前,小編先提一個問題。
請問在Spring中,如果JVM異常終止,Spring是如何保證會釋放掉佔用的資源,比如說數據庫連接等資源呢?
鉤子函數非常簡單,簡單到小編只用摘抄一段Spring代碼即可。走你,現在開始。
問題
Spring
容器中 Bean
在什麼時候執行銷燬方法?
我們知道在Spring中定義銷燬方法有兩種方式
- 實現
DisposableBean
的destroy
方法。 - 使用
@PreDestroy
註解修飾方法
@Component
public class DataCollectBean implements DisposableBean {
/**
* 第一種方法實現 DisposableBean#destroy方法
*
* @throws Exception 異常
*/
@Override
public void destroy() throws Exception {
System.err.println("執行銷燬方法");
}
/**
* 第二種方法使用PreDestroy註解聲明銷燬方法
*/
@PreDestroy
public void customerDestroy() {
System.err.println("執行自定義銷燬方法");
}
}
那麼在什麼時候執行銷燬方法?
- 主動執行銷燬bean
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
DataCollectBean bean = run.getBean(DataCollectBean.class);
//1. 主動銷燬bean
run.getBeanFactory().destroyBean(bean);
}
- JVM關閉時候自動執行銷燬方法。
這裏就要用到鉤子函數了, Spring
的鉤子函數在 AbstractApplicationContext#shutdownHook屬性
如果我們是SpringBoot項目我們看到在SpringApplication啓動時候會註冊一個鉤子函數
如何定義鉤子函數?
簡直太簡單了,沒有任何學習成本。一行代碼就能搞定。
public class HooksTester {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("鉤子函數執行");
}
}));
//當主動關閉應用
while (true);
}
}
觸發鉤子函數的場景
只要不是機器斷電,強制kill -9 強制殺進程,都會觸發。
鉤子函數能做什麼?
正如上圖所示優雅停機,在項目將要關閉時候,主動釋放程序佔用的資源信息,釋放db連接池的連接等其他佔用的資源信息。
如果我們是 Spring
項目其實我們不用自己定義鉤子函數,我們只要使用Spring提供給我們的銷燬方法即可。因爲
Spring定義的鉤子函數中會去執行, DisposableBean.destory()
和被 PreDestroy
修飾的方法。
我們看下源碼
protected void doClose() {
// Check whether an actual close attempt is necessary...
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (logger.isDebugEnabled()) {
logger.debug("Closing " + this);
}
LiveBeansView.unregisterApplicationContext(this);
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
// Stop all Lifecycle beans, to avoid delays during individual destruction.
if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
}
catch (Throwable ex) {
logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
}
}
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// Close the state of this context itself.
closeBeanFactory();
// Let subclasses do some final clean-up if they wish...
onClose();
// Reset local application listeners to pre-refresh state.
if (this.earlyApplicationListeners != null) {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Switch to inactive.
this.active.set(false);
}
}
可以看到:doClose()方法會執行bean的destroy(),也會執行SmartLifeCycle的stop()方法,我們就可以通過重寫這些方法來實現對象的關閉,生命週期的管理,實現平滑shutdown
感謝您的閱讀,本文由 程序猿升級課 版權所有。如若轉載,請註明出處:程序猿升級課(https://blog.springlearn.cn/)