在本文中,我們探討什麼是原型 Bean,以及 Spring 如何處理初始化,然後讓客戶端來處理銷燬。
雖然可能不需要手動銷燬原型 bean,但如果它們處理檔案處理、資料庫連線或網路等資源,則建議這樣做。由於每次請求時都會建立原型 bean 例項,因此資源會很快堆積起來。為了避免任何不必要的問題(例如記憶體洩漏),我們必須釋放資源。
我們可以用來銷燬 bean 的四種方法,包括@PreDestroy、DisposableBean介面、DestructionAwareBeanPostProcessor介面和自定義方法。
原型Bean及其生命週期
作用域決定了 bean 在其存在上下文中的生命週期和可見性。根據其定義的範圍,IoC 容器負責管理 bean 的生命週期。
原型作用域規定,每次使用getBean()請求或注入另一個 bean時,容器都會建立一個新的 bean 例項。在建立和初始化的情況下,我們可以安全地依賴 Spring。但是,銷燬 bean 的過程有所不同。
在檢查銷燬bean的必要性之前,我們先來看一下如何建立原型bean:
@Component |
原型Bean需要手動銷燬嗎?
Spring 不會自動銷燬原型 bean。與單例範圍不同,在單例範圍內,IoC 容器負責 bean 的整個生命週期,而對於原型,情況並非如此。容器將例項化、配置和組裝原型 bean,但隨後它將停止跟蹤其狀態。
在 Java 中,當物件不再透過任何引用可訪問時,它就符合垃圾回收條件。通常,在使用完原型 bean 例項後,將其保留下來以供垃圾回收器回收就足夠了。換句話說,在大多數情況下,我們不必費心銷燬原型 bean。
另一方面,讓我們考慮建議手動銷燬 bean 的情況。例如,在處理需要資源的程序(如處理檔案、資料庫連線或網路)時。由於原型範圍規定每次使用 bean 時都會建立一個 bean,這意味著資源也會被利用和消耗。因此,隨著時間的推移,使用量的積累可能會導致潛在的問題,例如記憶體洩漏和連線池耗盡。發生這種情況的原因是我們從不釋放這些資源,我們只是使用原型 bean 不斷建立新的資源。
這就是為什麼我們必須確保在使用原型 bean 之後正確地銷燬它們,並關閉我們建立或使用的所有資源。
如何銷燬原型Bean?
有幾種方法可以在 Spring 中手動銷燬 bean。需要注意的是,如果我們使用多種機制,容器將應用每種機制,但我們至少需要使用一種。
每個示例都需要手動呼叫BeanFactory 中的destroyBean()方法,但自定義方法除外,我們可以呼叫自定義方法。我們將從ApplicationContext獲取BeanFactory並呼叫 bean 銷燬:
applicationContext.getBeanFactory().destroyBean(prototypeBean);
1. 使用@PreDestroy註解
註釋@PreDestroy用於標記負責銷燬 bean 的 bean 方法。方法不允許有任何引數,也不允許是靜態的。我們將在實踐中看到它是什麼樣子:
@Component |
2. DisposableBean介面
DisposableBean介面有一個回撥方法destroy(),我們必須實現它。Spring 團隊不建議使用DisposableBean介面,因為它將程式碼與 Spring 耦合在一起。不過,我們還是來看看如何使用它:
@Component |
3. DestructionAwareBeanPostProcessor介面
DestructionAwareBeanPostProcessor與其他BeanPostProcessor變體一樣,可自定義 bean 的初始化。一個關鍵區別是,它包含一個額外的方法,用於在銷燬 bean 之前執行自定義邏輯。
在實現介面之前,我們必須確保有辦法從 bean 中釋放資源。我們可以使用 DisposableBean(如上例所示)或自定義方法。
下一步是實現一個介面,我們將在其中呼叫我們的銷燬方法:
@Component |
4. 使用 POJO 的自定義方法
可能存在這樣的情況,我們有一個POJO,我們想將其定義為原型 bean。在定義 bean 時,我們可以使用屬性destroyMethod來指定負責銷燬 bean 的特定方法。讓我們看看如何做到這一點:
public class CustomMethodBeanExample { |
我們成功地將自定義方法標記為destroyMethod回撥,但它永遠不會被呼叫。這是因為容器只對生命週期完全由它控制的 bean 呼叫它。在這種情況下,我們可以使用DestructionAwareBeanPostProcessor,或者在停止使用原型 bean 時簡單地呼叫我們的自定義銷燬方法。