Spring原型Bean需要手動銷毀嗎?
一、簡介
在本教程中,我們將探討 Spring 框架如何處理原型 bean 並管理它們的生命週期。了解如何使用 bean 及其範圍是應用程式開發的一個重要且有用的方面。我們將了解是否有必要手動銷毀原型 bean、何時以及如何執行。
儘管 Spring 為我們提供了各種有用的 bean 範圍,但原型將是本課的主要主題。
2. 原型Bean及其生命週期
範圍決定了 bean 的生命週期和它所在上下文中的可見性。根據其定義的範圍,IoC容器負責管理bean的生命週期。原型範圍規定容器每次使用getBean()
請求或註入另一個 bean 時都會建立新的 bean 實例。在創建和初始化的情況下,我們可以放心地依賴Spring。然而,銷毀豆子的過程是不同的。
在我們檢查銷毀bean的必要性之前,讓我們先看看如何創建原型bean:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeExample {
}
3.原型Bean需要手動銷毀嗎?
Spring 不會自動銷毀原型 bean。與單例作用域不同,在單例作用域中,IoC 容器透過原型來處理 bean 的整個生命週期,但事實並非如此。容器將實例化、配置和組裝原型 bean,但隨後它將停止追蹤其狀態。
在 Java 中,當無法透過任何引用存取某個物件時,該物件就可以進行垃圾回收。通常,在使用後留下原型 bean 實例以供垃圾收集器拾取它就足夠了。換句話說,在大多數用例中我們不必費心去破壞原型 bean。
另一方面,讓我們考慮一下建議手動銷毀 Bean 的場景。例如,在處理需要資源(例如處理檔案、資料庫連線或網路)的進程時。由於原型範圍聲明每次使用 bean 時都會建立一個 bean,這表示資源也會被利用和消耗。因此,隨著時間的推移使用量的累積可能會導致潛在的問題,例如記憶體洩漏和連接池耗盡。發生這種情況是因為我們從不釋放這些資源,我們只是透過使用原型 bean 來不斷建立新資源。
這就是為什麼我們必須確保在使用原型 bean 後正確銷毀它們,關閉我們創建或使用的所有資源。
4. 如何銷毀Prototype Bean?
有多種方法可以手動銷毀 Spring 中的 bean。需要注意的是,如果我們使用多種機制,容器將應用每種機制,但我們至少需要使用一種。
每個範例都需要從BeanFactory
手動呼叫destroyBean()
方法,但在自訂方法方法中我們可以呼叫自訂方法。我們將從ApplicationContext
取得BeanFactory
並呼叫 bean 來銷毀:
applicationContext.getBeanFactory().destroyBean(prototypeBean);
4.1.使用@PreDestroy
註解
註解@PreDestroy
用於標記我們的bean的方法,該方法負責銷毀bean。方法不允許有任何參數,也不允許是靜態的。我們將看看實際情況如何:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PreDestroyBeanExample {
@PreDestroy
private void destroy() {
// release all resources that the bean is holding
}
}
4.2. DisposableBean
接口
DisposableBean
介面有一個單一的回呼方法destroy(),
我們必須實作它。 Spring團隊不建議使用DisposableBean
介面,因為它將程式碼耦合到Spring。儘管如此,我們還是來看看如何使用它:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DisposableBeanExample implements DisposableBean {
@Override
public void destroy() {
// release all resources that the bean is holding
}
}
4.3. DestructionAwareBeanPostProcessor
介面
DestructionAwareBeanPostProcessor
與其他BeanPostProcessor
變體一樣,自訂 bean 的初始化。一個關鍵的區別是它包含一種在銷毀 bean 之前執行自訂邏輯的附加方法。
在實作介面之前,我們必須確保有一種方法可以從 bean 中釋放資源。我們可以使用 DisposableBean(如前面的範例所示)或自訂方法。
下一步是實作一個接口,我們將在其中呼叫我們的銷毀方法:
@Component
public class CustomPostProcessor implements DestructionAwareBeanPostProcessor {
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (bean instanceof PostProcessorBeanExample) {
((PostProcessorBeanExample) bean).destroy();
}
}
}
4.4. POJO 的自訂方法
可能存在這樣一種情況:我們想要將 POJO 定義為原型 bean。在定義 bean 時,我們可以使用屬性destroyMethod
來指定負責銷毀 bean 的特定方法。讓我們看看這是如何完成的:
public class CustomMethodBeanExample {
public void destroy() {
// release all resources that the bean is holding
}
}
@Configuration
public class DestroyMethodConfig {
@Bean(destroyMethod = "destroy")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public CustomMethodBeanExample customMethodBeanExample() {
return new CustomMethodBeanExample();
}
}
我們成功地將自訂方法標記為destroyMethod
回調,但它永遠不會被呼叫。這是因為容器僅為其生命週期完全控制的 bean 調用它。在這樣的場景中,我們可以利用DestructionAwareBeanPostProcessor或在停止使用原型 bean 時簡單地呼叫我們的自訂銷毀方法。
5. 結論
在本文中,我們探討了什麼是原型 bean 以及 Spring 如何處理初始化,然後讓客戶端負責銷毀。
儘管可能沒有必要手動銷毀原型 bean,但如果它們處理文件處理、資料庫連接或網路等資源,建議您這樣做。由於每次請求時都會建立原型 bean 實例,因此我們很快就會累積資源。為了避免任何不必要的問題,例如記憶體洩漏,我們必須釋放資源。
我們學習了幾種可用於銷毀 bean 的方法,包括@PreDestroy
、 DisposableBean
介面、 DestructionAwareBeanPostProcessor
介面和自訂方法。
與往常一樣,完整的程式碼範例可以在 GitHub 上找到。