Spring Bean 生命週期與原型作用域

最後更新: 2025-11-17 23:19:46 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

Spring 將 Bean 中繼資料與 Bean 執行個體分離。BeanDefinition 描述如何建立物件, 而 BeanFactory 會在要求物件時套用該定義。ApplicationContext 另外提供協調式啟動、 後處理、事件、資源載入與關閉功能。

Context 重新整理期間會發生什麼 #

AbstractApplicationContext.refresh() 會在應用程式單例可以使用之前準備環境與 Bean 工廠。主要階段如下:

  1. 準備或取得 BeanFactory,並載入 Bean 定義。
  2. 在定義仍可變更時呼叫 BeanFactoryPostProcessors,包括組態類別處理。
  3. 探索並註冊 BeanPostProcessors。
  4. 初始化 Context 基礎設施,例如訊息解析、事件及 Context 專用資源。
  5. 建立其餘的非延遲初始化單例 Bean。
  6. 發布已重新整理的狀態與 ContextRefreshedEvent。

BeanFactoryPostProcessor 會變更中繼資料。相較之下,BeanPostProcessor 會處理 執行個體,可以注入欄位、呼叫生命週期註解,或將 Bean 替換為 Proxy。由於一般應用程式 Bean 可能在所有後處理器註冊前就被建立,因此從工廠後處理器建立一般應用程式 Bean 並不安全。

相依順序與 depends-on #

容器會先建立 Bean 的建構函式相依項與屬性相依項,再建立相依於它的 Bean。因此, 建構函式注入同時表達必要關係與建立順序:

java
@Component
final class ReportService {
    private final ReportRepository repository;

    ReportService(ReportRepository repository) {
        this.repository = repository;
    }
}

當不存在注入關係時,例如初始化共用外部資源,@DependsOn("schemaInitializer") 或 XML 的 depends-on 可以加入順序。對單例 Bean 而言,它也會反轉銷毀順序: 相依 Bean 會在其所相依的 Bean 之前被銷毀。depends-on 會隱藏類別所使用的內容, 因此不應取代實際的具型別相依關係。

執行個體初始化 #

對一般 Bean 而言,Spring 會執行以下操作:

  1. 建立物件,通常是呼叫建構函式或工廠方法。
  2. 解析並注入屬性與欄位。
  3. 呼叫 aware 回呼,包括 BeanNameAware、BeanFactoryAware 與 ApplicationContextAware。
  4. 在初始化前呼叫每個 BeanPostProcessor。
  5. 依序呼叫 @PostConstruct、InitializingBean.afterPropertiesSet(), 然後呼叫已設定的自訂 init 方法。
  6. 在初始化後呼叫每個 BeanPostProcessor。

Spring 基礎設施透過後處理器實作此序列的部分步驟,因此自訂處理器必須遵守排序契約。 初始化後處理器可能返回 Proxy,而不是原始物件。只有在完整鏈結完成後,呼叫端才會收到 已準備完成、可能已套用建議的物件。

建構函式注入是建立必要不變量最安全的位置。當初始化依賴已注入的屬性或建構函式中 無法取得的容器服務時,適合使用 @PostConstruct。

銷毀與 Context 關閉 #

關閉可設定的 Context 會銷毀受管理的單例。對每個 Bean,Spring 會呼叫 @PreDestroy、 DisposableBean.destroy() 及已設定的自訂 destroy 方法,但會受配接器的重複方法檢查約束。 SmartLifecycle 為需要協調關閉的元件加入分階段的停止回呼。

應用程式應關閉手動建立的 Context,或使用 registerShutdownHook() 註冊關閉勾點。 Spring Boot 會在程序正常關閉時關閉其應用程式 Context。程序突然終止時無法保證回呼執行, 因此持久性正確性不得單獨依賴這些回呼。

原型的擁有權 #

原型作用域表示每次容器要求都會產生新的執行個體。當原型直接注入單例時,只會在建立 單例期間解析一次;該欄位會保留相同的原型執行個體。如果每個操作都需要新的執行個體, 請使用 ObjectProvider<T>、Jakarta Provider<T>、方法注入或作用域 Proxy。

Spring 會完整初始化原型並將其返回給呼叫端,但不會保留該執行個體,也不會在之後呼叫 銷毀回呼。因此,擁有權會移交給接收者。取得持有檔案、Socket 或執行器之原型的程式碼 必須明確關閉它。如果需要集中式清理,自訂 BeanPostProcessor 可以追蹤此類執行個體, 但明確的資源擁有權更加清楚。

主要參考資料 #

評論

在 GitHub 上閱讀或評論(發表評論需要登入)