知識庫 / Spring RSS 訂閱

Spring 容器中原型 Bean 是否需要手動銷燬?

Spring
HongKong
5
11:05 AM · Dec 06 ,2025

1. 介紹

在本教程中,我們將探討 Spring 框架如何處理原型 Bean 以及如何管理它們的生命週期。理解如何使用 Bean 以及它們的範圍是應用程序開發中一個重要且有用的方面。我們將發現手動銷燬原型 Bean 是否必要,何時以及如何執行它。

雖然 Spring 提供了各種有用的 Bean 範圍,但原型範圍將是本課程的主要內容。

2. 原型 Bean 及生命週期

Scope 決定了 Bean 在其存在的上下文中可見性和生命週期。根據其定義的 Scope,IoC 容器負責管理 Bean 的生命週期。 原型 Scope 規定,每次使用 getBean() 或將其注入到另一個 Bean 中,容器都會創建一個新的 Bean 實例。 在創建和初始化過程中,我們可以安全地依賴 Spring。但是,銷燬 Bean 的過程則有所不同。

在檢查銷燬 Bean 的必要性之前,我們首先來看一下如何創建原型 Bean:

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeExample {
}

3. 試用Bean是否需要手動銷燬?

Spring 不會自動銷燬試用Bean。與單例作用域不同,IoC容器會管理Bean的整個生命週期,而對於試用Bean,情況並非如此。 容器會實例化、配置和組裝試用Bean,但隨後它將不再跟蹤其狀態。

在Java中,當對象不再可以通過任何引用訪問時,它就成為垃圾收集器的目標。對於大多數用例來説,只需在試用Bean的使用完畢後,讓垃圾收集器將其回收即可。 換句話説,我們通常不需要銷燬試用Bean。

另一方面,讓我們考慮在何時手動銷燬Bean的情況。例如,在處理需要資源的情況,例如處理文件、數據庫連接或網絡連接。由於試用Bean的作用域意味着每次使用時都會創建一個Bean,因此資源也會被利用和消耗。 因此,隨着時間的推移,使用量積累可能導致潛在問題,例如內存泄漏和連接池耗盡。這發生在因為我們從未釋放這些資源,只是通過使用試用Bean創建了新的資源。

這就是為什麼我們必須在使用完試用Bean後,確保正確地銷燬它們,關閉我們創建或使用的所有資源。

4. 如何手動銷燬 Prototype Bean?

有幾種方法可以手動銷燬 Spring 中的 Bean。需要注意的是,容器會應用所有機制,如果同時使用多個機制,但至少需要使用一個。

每個示例都需要手動調用 destroyBean() 方法,該方法來自 BeanFactory,除非在自定義方法中,我們調用自定義方法。 我們將從 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. <em>DisposableBean</em> 接口

<em>DisposableBean</em> 接口包含一個唯一的回調方法 <em>destroy()</em>,我們需要實現它。Spring 團隊不建議使用 <em>DisposableBean</em> 接口,因為它將代碼與 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. <em >DestructionAwareBeanPostProcessor</em> 接口

<em >DestructionAwareBeanPostProcessor</em>,與其他 <em >BeanPostProcessor</em> 變體類似,自定義 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 的方法,包括 <em @PreDestroy</em>>, <em DisposableBean 接口</em>>, <em DestructionAwareBeanPostProcessor 接口</em>> 以及自定義方法。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.