DestructionAwareBeanPostProcessor
一、基本信息
?? 作者 - Lex ?? 博客 - 我的CSDN ?? 文章目錄 - 所有文章 ?? 源碼地址 - DestructionAwareBeanPostProcessor源碼
二、接口描述
DestructionAwareBeanPostProcessor 接口,用于提供在 bean 銷毀之前進(jìn)行額外處理或操作的機(jī)會。其主要職責(zé)是在 bean 即將被銷毀時(shí)允許執(zhí)行自定義的邏輯。
三、接口源碼
DestructionAwareBeanPostProcessor 是 Spring 框架自 1.0.1 版本開始引入的一個(gè)核心接口。為特定的 bean 類型調(diào)用自定義的初始化和銷毀回調(diào)。這提供了一個(gè)機(jī)制,允許我們介入 Spring bean 的生命周期,在銷毀階段執(zhí)行特定邏輯。
/**
* BeanPostProcessor的子接口,增加了銷毀前的回調(diào)。
*
* 典型的用途是在特定的bean類型上調(diào)用自定義的銷毀回調(diào),
* 與相應(yīng)的初始化回調(diào)相匹配。
*
* @author Juergen Hoeller
* @since 1.0.1
*/
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
/**
* 在給定的 bean 實(shí)例銷毀之前應(yīng)用此 BeanPostProcessor,
* 例如,調(diào)用自定義的銷毀回調(diào)。
* 與 DisposableBean 的 {@code destroy} 方法和一個(gè)自定義的銷毀方法一樣,此回調(diào)
* 僅適用于容器完全管理其生命周期的 beans。這通常適用于單例和有作用域的 beans。
* @param bean 要被銷毀的 bean 實(shí)例
* @param beanName bean 的名稱
* @throws org.springframework.beans.BeansException 如果發(fā)生錯(cuò)誤
* @see org.springframework.beans.factory.DisposableBean#destroy()
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#setDestroyMethodName(String)
*/
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
/**
* 確定給定的 bean 實(shí)例是否需要由此后處理器銷毀。
* 默認(rèn)實(shí)現(xiàn)返回true。如果一個(gè)基于 pre-5 的 DestructionAwareBeanPostProcessor
* 實(shí)現(xiàn)沒有為此方法提供具體實(shí)現(xiàn),Spring 也會默默地假設(shè)返回值為 true。
* @param bean 要檢查的 bean 實(shí)例
* @return 如果需要為此 bean 實(shí)例最終調(diào)用 postProcessBeforeDestruction,返回 true,否則返回 false
* @since 4.3
*/
default boolean requiresDestruction(Object bean) {
return true;
}
}
四、主要功能
-
銷毀前邏輯
- 使用
postProcessBeforeDestruction(Object bean, String beanName)方法,我們可以為 bean 執(zhí)行自定義的銷毀邏輯。當(dāng)一個(gè) bean 被容器標(biāo)記為銷毀時(shí),此方法將被調(diào)用。(例如,容器關(guān)閉時(shí)進(jìn)行資源釋放,狀態(tài)記錄,依賴清理)
- 使用
五、最佳實(shí)踐
首先來看看啟動類入口,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式),構(gòu)造參數(shù)我們給定了一個(gè)MyConfiguration組件類,然后從Spring上下文中獲取一個(gè)ConnectionService類型的bean,并打印isConnected的狀態(tài)。
public class DestructionAwareBeanPostProcessorApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
ConnectionService connection = context.getBean("connectionService", ConnectionService.class);
System.out.println("Is connected: " + connection.isConnected());
context.close();
}
}
這里使用@Bean注解,定義了兩個(gè)Bean,是為了確保ConnectionService, MyDestructionAwareBeanPostProcessor 被 Spring 容器執(zhí)行
@Configuration
public class MyConfiguration {
@Bean
public static MyDestructionAwareBeanPostProcessor myDestructionAwareBeanPostProcessor() {
return new MyDestructionAwareBeanPostProcessor();
}
@Bean
public ConnectionService connectionService() {
return new ConnectionServiceImpl();
}
}
MyDestructionAwareBeanPostProcessor 類的目的是管理 ConnectionServiceImpl bean 的生命周期。當(dāng)這個(gè) bean 初始化完成后,它的連接被打開;當(dāng)這個(gè) bean 準(zhǔn)備被銷毀時(shí),它的連接被關(guān)閉。這確保了資源在不再需要時(shí)被適當(dāng)?shù)蒯尫拧?/p>
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ConnectionServiceImpl) {
((ConnectionServiceImpl) bean).openConnection();
}
return bean;
}
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (bean instanceof ConnectionServiceImpl) {
((ConnectionServiceImpl) bean).closeConnection();
}
}
@Override
public boolean requiresDestruction(Object bean) {
return (bean instanceof ConnectionServiceImpl);
}
}
定義一個(gè)連接的服務(wù)接口
public interface ConnectionService {
void openConnection();
void closeConnection();
boolean isConnected();
}
ConnectionServiceImpl 類提供了一個(gè)模擬的連接服務(wù)。它可以跟蹤其連接狀態(tài),并允許調(diào)用者打開和關(guān)閉連接,以及查詢連接的狀態(tài)。
public class ConnectionServiceImpl implements ConnectionService {
private boolean isConnected = false;
@Override
public void openConnection() {
isConnected = true;
System.out.println("connection opened.");
}
@Override
public void closeConnection() {
if (isConnected) {
isConnected = false;
System.out.println("connection closed.");
}
}
@Override
public boolean isConnected() {
return isConnected;
}
}
運(yùn)行結(jié)果發(fā)現(xiàn),由于在 MyDestructionAwareBeanPostProcessor 的 postProcessAfterInitialization 方法中,我們檢測到 bean 是 ConnectionServiceImpl 的實(shí)例并調(diào)用了其 openConnection 方法,因此該連接被打開。然后我們在DestructionAwareBeanPostProcessorApplication類的main方法中調(diào)用 isConnected 方法并打印結(jié)果的直接效果。這證明了在應(yīng)用上下文啟動和運(yùn)行期間,連接確實(shí)是打開的。由于在 MyDestructionAwareBeanPostProcessor 的 postProcessBeforeDestruction 方法中,我們檢測到 bean 是 ConnectionServiceImpl 的實(shí)例并調(diào)用了其 closeConnection 方法,因此該連接被關(guān)閉。
connection opened.
Is connected: true
connection closed.
六、時(shí)序圖
sequenceDiagram
Title: DestructionAwareBeanPostProcessor時(shí)序圖
participant DestructionAwareBeanPostProcessorApplication
participant AnnotationConfigApplicationContext
participant AbstractApplicationContext
participant DefaultListableBeanFactory
participant DefaultSingletonBeanRegistry
participant DisposableBeanAdapter
participant MyDestructionAwareBeanPostProcessor
DestructionAwareBeanPostProcessorApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses )<br>應(yīng)用開始初始化上下文
AnnotationConfigApplicationContext-->>DestructionAwareBeanPostProcessorApplication:初始化完成
DestructionAwareBeanPostProcessorApplication->>AbstractApplicationContext:close()<br>請求關(guān)閉上下文
AbstractApplicationContext->>AbstractApplicationContext:doClose()<br>執(zhí)行關(guān)閉邏輯
AbstractApplicationContext->>AbstractApplicationContext:destroyBeans()<br>開始銷毀beans
AbstractApplicationContext->>DefaultListableBeanFactory:destroySingletons()<br>銷毀單例beans
DefaultListableBeanFactory->>DefaultSingletonBeanRegistry:super.destroySingletons()<br>調(diào)父類銷毀方法
DefaultSingletonBeanRegistry-->>DefaultListableBeanFactory:destroySingleton(beanName)<br>銷毀單個(gè)bean
DefaultListableBeanFactory->>DefaultSingletonBeanRegistry:super.destroySingleton(beanName)<br>調(diào)父類銷毀bean方法
DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:destroyBean(beanName,bean)<br>執(zhí)行銷毀bean操作
DefaultSingletonBeanRegistry->>DisposableBeanAdapter:destroy()<br>適配器執(zhí)行銷毀
DisposableBeanAdapter->>MyDestructionAwareBeanPostProcessor:postProcessBeforeDestruction(bean,beanName)<br>執(zhí)行自定義銷毀邏輯
AbstractApplicationContext-->>DestructionAwareBeanPostProcessorApplication:請求關(guān)閉上下文結(jié)束
七、源碼分析
首先來看看啟動類入口,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式),構(gòu)造參數(shù)我們給定了一個(gè)MyConfiguration組件類。然后從Spring上下文中獲取一個(gè)ConnectionService類型的bean,并打印isConnected的狀態(tài)。
public class DestructionAwareBeanPostProcessorApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
ConnectionService connection = context.getBean("connectionService", ConnectionService.class);
System.out.println("Is connected: " + connection.isConnected());
context.close();
}
}
在org.springframework.context.support.AbstractApplicationContext#close方法中,首先是啟動了一個(gè)同步塊,它同步在 startupShutdownMonitor 對象上。這確保了在給定時(shí)刻只有一個(gè)線程可以執(zhí)行這個(gè)塊內(nèi)的代碼,防止多線程導(dǎo)致的資源競爭或數(shù)據(jù)不一致,然后是調(diào)用了 doClose 方法,最后是為 JVM 注冊了一個(gè)關(guān)閉鉤子。
@Override
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
// If we registered a JVM shutdown hook, we don't need it anymore now:
// We've already explicitly closed the context.
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}
catch (IllegalStateException ex) {
// ignore - VM is already shutting down
}
}
}
}
在org.springframework.context.support.AbstractApplicationContext#doClose方法中,又調(diào)用了 destroyBeans 方法。
protected void doClose() {
// ... [代碼部分省略以簡化]
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// ... [代碼部分省略以簡化]
}
在org.springframework.context.support.AbstractApplicationContext#destroyBeans方法中,首先是調(diào)用了getBeanFactory()返回 Spring 的 BeanFactory ,然后在獲得的 BeanFactory 上,調(diào)用了 destroySingletons 方法,這個(gè)方法的目的是銷毀所有在 BeanFactory 中緩存的單例 beans。
protected void destroyBeans() {
getBeanFactory().destroySingletons();
}
在org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons方法中,首先是調(diào)用了父類的 destroySingletons 方法,為了確保繼承自父類的銷毀邏輯得到了執(zhí)行。
@Override
public void destroySingletons() {
super.destroySingletons();
updateManualSingletonNames(Set::clear, set -> !set.isEmpty());
clearByTypeCache();
}
在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons方法中,首先是在disposableBeans 字段上,從其鍵集合中獲取所有的 bean 名稱,并將它們轉(zhuǎn)換為一個(gè)字符串?dāng)?shù)組。disposableBeans 可能包含了實(shí)現(xiàn)了 DisposableBean 接口的 beans,這些 beans 需要在容器銷毀時(shí)特殊處理,最后倒序循環(huán),從最后一個(gè)開始,銷毀所有在 disposableBeans 列表中的 beans。這樣做是為了確保依賴關(guān)系正確地處理,beans先被創(chuàng)建的應(yīng)該后被銷毀。
public void destroySingletons() {
// ... [代碼部分省略以簡化]
String[] disposableBeanNames;
synchronized (this.disposableBeans) {
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
}
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
destroySingleton(disposableBeanNames[i]);
}
// ... [代碼部分省略以簡化]
}
在org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingleton方法中,首先是調(diào)用了父類的 destroySingleton 方法,為了確保繼承自父類的銷毀邏輯得到了執(zhí)行。
@Override
public void destroySingleton(String beanName) {
super.destroySingleton(beanName);
removeManualSingletonName(beanName);
clearByTypeCache();
}
在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton方法中,首先是使用 synchronized 關(guān)鍵字在 disposableBeans 對象上進(jìn)行同步,以確保在多線程環(huán)境中安全地訪問和修改它,從 disposableBeans 集合中移除指定名稱的 bean,并將其轉(zhuǎn)換為 DisposableBean 類型,最后調(diào)用destroyBean方法執(zhí)行實(shí)際的銷毀操作。
public void destroySingleton(String beanName) {
// Remove a registered singleton of the given name, if any.
removeSingleton(beanName);
// Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized (this.disposableBeans) {
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean方法中,直接調(diào)用 bean 的 destroy 方法。因?yàn)?bean 是一個(gè) DisposableBean 類型的實(shí)例,所以它一定有一個(gè) destroy 方法,該方法提供了 bean 的自定義銷毀邏輯。
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
// ... [代碼部分省略以簡化]
// Actually destroy the bean now...
if (bean != null) {
try {
bean.destroy();
}
catch (Throwable ex) {
// ... [代碼部分省略以簡化]
}
}
// ... [代碼部分省略以簡化]
}
在org.springframework.beans.factory.support.DisposableBeanAdapter#destroy方法中,遍歷 beanPostProcessors 集合的循環(huán)。每一個(gè)元素都是 DestructionAwareBeanPostProcessor 類型,它是 BeanPostProcessor 的一個(gè)子接口,專門為銷毀階段提供了一個(gè)回調(diào)。
@Override
public void destroy() {
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
// ... [代碼部分省略以簡化]
}
最后來到我們自定義的銷毀com.xcs.spring.config.MyDestructionAwareBeanPostProcessor#postProcessBeforeDestruction方法中,在這個(gè)方法中,如果 bean 是 ConnectionServiceImpl 的一個(gè)實(shí)例,則該 bean 的 closeConnection 方法將被調(diào)用。這確保了每當(dāng) ConnectionServiceImpl 類型的 bean 被銷毀之前,它的連接都會被關(guān)閉。
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ConnectionServiceImpl) {
((ConnectionServiceImpl) bean).openConnection();
}
return bean;
}
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (bean instanceof ConnectionServiceImpl) {
((ConnectionServiceImpl) bean).closeConnection();
}
}
@Override
public boolean requiresDestruction(Object bean) {
return (bean instanceof ConnectionServiceImpl);
}
}
八、注意事項(xiàng)
-
性能影響
- 每一個(gè)
DestructionAwareBeanPostProcessor在 bean 的生命周期結(jié)束時(shí)都會被調(diào)用,所以應(yīng)該確保其中的代碼高效執(zhí)行,避免產(chǎn)生不必要的性能瓶頸。
- 每一個(gè)
-
檢查
requiresDestruction- 實(shí)現(xiàn)
requiresDestruction方法來指定哪些 beans 需要在銷毀時(shí)進(jìn)行處理,可以避免不必要的postProcessBeforeDestruction調(diào)用,從而提高性能。
- 實(shí)現(xiàn)
-
異常處理
- 在
postProcessBeforeDestruction方法中可能會遇到任何類型的異常,應(yīng)確保適當(dāng)?shù)靥幚磉@些異常,以避免影響其他 beans 的銷毀。
- 在
-
確保與其他
BeanPostProcessors協(xié)調(diào)- 如果我們的應(yīng)用程序中還有其他
BeanPostProcessors,確保它們之間的相互作用不會導(dǎo)致問題。
- 如果我們的應(yīng)用程序中還有其他
-
與
@PreDestroy注解協(xié)同工作- 如果 bean 已經(jīng)使用了
@PreDestroy注解來定義自己的銷毀方法,這些方法會在postProcessBeforeDestruction被調(diào)用之前執(zhí)行。確保這兩者的邏輯不會互相干擾。
- 如果 bean 已經(jīng)使用了
八、總結(jié)
最佳實(shí)踐總結(jié)
-
應(yīng)用啟動:
在
DestructionAwareBeanPostProcessorApplication的main方法中,首先創(chuàng)建了一個(gè)AnnotationConfigApplicationContext上下文,并通過MyConfiguration類進(jìn)行配置。通過該上下文獲取了一個(gè)名為
connectionService的ConnectionService類型的 bean。
-
Spring容器的初始化:
在初始化過程中,Spring容器將根據(jù)
MyConfiguration類創(chuàng)建兩個(gè) beans:MyDestructionAwareBeanPostProcessor和ConnectionServiceImpl。當(dāng)
ConnectionServiceImplbean 初始化完成后,MyDestructionAwareBeanPostProcessor的postProcessAfterInitialization方法被調(diào)用,此時(shí)連接被打開。MyDestructionAwareBeanPostProcessor的作用確保了ConnectionServiceImplbean 初始化完成后會立即打開連接。
-
應(yīng)用運(yùn)行期:
在
main方法中,應(yīng)用查詢了ConnectionServicebean 的連接狀態(tài),并打印出結(jié)果,顯示連接為 "打開" 狀態(tài)。隨后,上下文被關(guān)閉,意味著所有的單例 bean 將被銷毀。
-
銷毀階段:
在上下文關(guān)閉時(shí),
MyDestructionAwareBeanPostProcessor的postProcessBeforeDestruction方法會被調(diào)用。由于我們在這個(gè)方法中檢查了 bean 是否是
ConnectionServiceImpl的實(shí)例,所以closeConnection方法被調(diào)用,從而關(guān)閉連接。這確保了在 bean 的生命周期結(jié)束時(shí),資源被適當(dāng)?shù)蒯尫拧?/p>
-
運(yùn)行結(jié)果:
- 最終,控制臺上的輸出證明了在 bean 的生命周期開始時(shí)資源被打開,在生命周期結(jié)束時(shí)資源被關(guān)閉。
源碼分析總結(jié)
-
應(yīng)用啟動:
應(yīng)用通過
AnnotationConfigApplicationContext啟動,并用MyConfiguration類進(jìn)行配置。然后從Spring容器中獲取了一個(gè)類型為
ConnectionService的 bean,并檢查其連接狀態(tài)。
-
Spring容器銷毀:
通過調(diào)用
context.close(),應(yīng)用啟動了Spring容器的關(guān)閉流程。關(guān)閉流程首先通過同步機(jī)制確保在任何時(shí)刻都只有一個(gè)線程能夠執(zhí)行關(guān)閉操作。
內(nèi)部調(diào)用
doClose方法來執(zhí)行實(shí)際的關(guān)閉邏輯。JVM 的關(guān)閉鉤子被移除,表示上下文已經(jīng)明確關(guān)閉。
-
銷毀Beans:
在
doClose方法中,destroyBeans方法被調(diào)用,它的主要作用是銷毀所有的單例 beans。destroyBeans方法進(jìn)而調(diào)用BeanFactory的destroySingletons方法。
-
銷毀單例Beans:
destroySingletons方法銷毀所有在BeanFactory中緩存的單例 beans。它首先獲取所有需要被銷毀的 beans 的名稱,然后反向遍歷這些 beans,確保依賴的 beans 先被銷毀。
對每一個(gè)需要被銷毀的 bean,
destroySingleton方法被調(diào)用。
-
執(zhí)行銷毀邏輯:
在
destroySingleton方法中,從disposableBeans列表中獲取到實(shí)現(xiàn)了DisposableBean接口的 bean,然后調(diào)用它的destroy方法。在
DisposableBeanAdapter的destroy方法中,所有注冊的DestructionAwareBeanPostProcessor將被遍歷,對每一個(gè)處理器,都會調(diào)用其postProcessBeforeDestruction方法。
-
自定義銷毀邏輯:
最終,我們的自定義
DestructionAwareBeanPostProcessor的postProcessBeforeDestruction方法被調(diào)用。在這個(gè)方法中,檢查 bean 是否是
ConnectionServiceImpl的實(shí)例。如果是,關(guān)閉它的連接。