DestructionAwareBeanPostProcessor源碼分析

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;
    }
}

四、主要功能

  1. 銷毀前邏輯
    • 使用 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),由于在 MyDestructionAwareBeanPostProcessorpostProcessAfterInitialization 方法中,我們檢測到 bean 是 ConnectionServiceImpl 的實(shí)例并調(diào)用了其 openConnection 方法,因此該連接被打開。然后我們在DestructionAwareBeanPostProcessorApplication類的main方法中調(diào)用 isConnected 方法并打印結(jié)果的直接效果。這證明了在應(yīng)用上下文啟動和運(yùn)行期間,連接確實(shí)是打開的。由于在 MyDestructionAwareBeanPostProcessorpostProcessBeforeDestruction 方法中,我們檢測到 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)用 beandestroy 方法。因?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)

  1. 性能影響

    • 每一個(gè) DestructionAwareBeanPostProcessor 在 bean 的生命周期結(jié)束時(shí)都會被調(diào)用,所以應(yīng)該確保其中的代碼高效執(zhí)行,避免產(chǎn)生不必要的性能瓶頸。
  2. 檢查 requiresDestruction

    • 實(shí)現(xiàn) requiresDestruction 方法來指定哪些 beans 需要在銷毀時(shí)進(jìn)行處理,可以避免不必要的 postProcessBeforeDestruction 調(diào)用,從而提高性能。
  3. 異常處理

    • postProcessBeforeDestruction 方法中可能會遇到任何類型的異常,應(yīng)確保適當(dāng)?shù)靥幚磉@些異常,以避免影響其他 beans 的銷毀。
  4. 確保與其他 BeanPostProcessors 協(xié)調(diào)

    • 如果我們的應(yīng)用程序中還有其他 BeanPostProcessors,確保它們之間的相互作用不會導(dǎo)致問題。
  5. @PreDestroy 注解協(xié)同工作

    • 如果 bean 已經(jīng)使用了 @PreDestroy 注解來定義自己的銷毀方法,這些方法會在 postProcessBeforeDestruction 被調(diào)用之前執(zhí)行。確保這兩者的邏輯不會互相干擾。

八、總結(jié)

最佳實(shí)踐總結(jié)

  1. 應(yīng)用啟動:

    • DestructionAwareBeanPostProcessorApplicationmain 方法中,首先創(chuàng)建了一個(gè) AnnotationConfigApplicationContext 上下文,并通過 MyConfiguration 類進(jìn)行配置。

    • 通過該上下文獲取了一個(gè)名為 connectionServiceConnectionService 類型的 bean。

  1. Spring容器的初始化:

    • 在初始化過程中,Spring容器將根據(jù) MyConfiguration 類創(chuàng)建兩個(gè) beans: MyDestructionAwareBeanPostProcessorConnectionServiceImpl

    • 當(dāng) ConnectionServiceImpl bean 初始化完成后,MyDestructionAwareBeanPostProcessorpostProcessAfterInitialization 方法被調(diào)用,此時(shí)連接被打開。

    • MyDestructionAwareBeanPostProcessor 的作用確保了 ConnectionServiceImpl bean 初始化完成后會立即打開連接。

  1. 應(yīng)用運(yùn)行期:

    • main 方法中,應(yīng)用查詢了 ConnectionService bean 的連接狀態(tài),并打印出結(jié)果,顯示連接為 "打開" 狀態(tài)。

    • 隨后,上下文被關(guān)閉,意味著所有的單例 bean 將被銷毀。

  1. 銷毀階段:

    • 在上下文關(guān)閉時(shí),MyDestructionAwareBeanPostProcessorpostProcessBeforeDestruction 方法會被調(diào)用。

    • 由于我們在這個(gè)方法中檢查了 bean 是否是 ConnectionServiceImpl 的實(shí)例,所以 closeConnection 方法被調(diào)用,從而關(guān)閉連接。

    • 這確保了在 bean 的生命周期結(jié)束時(shí),資源被適當(dāng)?shù)蒯尫拧?/p>

  1. 運(yùn)行結(jié)果:
    • 最終,控制臺上的輸出證明了在 bean 的生命周期開始時(shí)資源被打開,在生命周期結(jié)束時(shí)資源被關(guān)閉。

源碼分析總結(jié)

  1. 應(yīng)用啟動:

    • 應(yīng)用通過 AnnotationConfigApplicationContext 啟動,并用 MyConfiguration 類進(jìn)行配置。

    • 然后從Spring容器中獲取了一個(gè)類型為 ConnectionService 的 bean,并檢查其連接狀態(tài)。

  1. 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)閉。

  1. 銷毀Beans:

    • doClose 方法中,destroyBeans 方法被調(diào)用,它的主要作用是銷毀所有的單例 beans。

    • destroyBeans 方法進(jìn)而調(diào)用 BeanFactorydestroySingletons 方法。

  1. 銷毀單例Beans:

    • destroySingletons 方法銷毀所有在 BeanFactory 中緩存的單例 beans。

    • 它首先獲取所有需要被銷毀的 beans 的名稱,然后反向遍歷這些 beans,確保依賴的 beans 先被銷毀。

    • 對每一個(gè)需要被銷毀的 bean,destroySingleton 方法被調(diào)用。

  1. 執(zhí)行銷毀邏輯:

    • destroySingleton 方法中,從 disposableBeans 列表中獲取到實(shí)現(xiàn)了 DisposableBean 接口的 bean,然后調(diào)用它的 destroy 方法。

    • DisposableBeanAdapterdestroy 方法中,所有注冊的 DestructionAwareBeanPostProcessor 將被遍歷,對每一個(gè)處理器,都會調(diào)用其 postProcessBeforeDestruction 方法。

  1. 自定義銷毀邏輯:

    • 最終,我們的自定義 DestructionAwareBeanPostProcessorpostProcessBeforeDestruction 方法被調(diào)用。

    • 在這個(gè)方法中,檢查 bean 是否是 ConnectionServiceImpl 的實(shí)例。如果是,關(guān)閉它的連接。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容