iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Software Development

spring boot 3 學習筆記系列 第 18

Day18 - Spring Boot 依賴注入指南:深入理解 Bean - 作用域與生命週期

  • 分享至 

  • xImage
  •  

今天要深入探討 Spring 框架中一個非常核心的概念:Spring 如何管理它所建立的物件 (我們稱之為 Bean)。具體來說,我們會學習兩件事:

  1. Bean 可以活多久、有多少個? — 這就是 作用域 (Scope)
  2. Bean 在出生和死亡時能做些什麼? — 這就是 生命週期回呼 (Lifecycle Callbacks)

Bean 的作用域 (Scope)

當我們請 Spring 幫我們管理一個類別的實例 (Instance) 時,我們需要告訴 Spring:「這個實例應該要有多少個?以及它應該在什麼範圍內共享?」這就是「作用域」的用途。

singleton (單例模式) - (預設)

這是 Spring 最常用、也是預設的作用域。它告訴 Spring:「在整個應用程式從啟動到關閉的過程中,這個類別只會有一個實例。

  • 白話解釋:想像一下,一間公司只有一間共用的會議室。無論是哪個部門、哪位員工要開會,他們都是預約並使用「同一間」會議室。
  • 特性:全局唯一、共享。因為只有一個實例,所以可以節省記憶體資源。
  • 適用場景:無狀態 (Stateless) 的服務,例如 Service 層、Repository 層、工具類、設定檔物件等。這些物件只提供方法,本身不儲存與特定使用者相關的資料。
@Service 
// @Scope("singleton") // 是預設值,所以可以省略
public class UserService {
    // 這個 UserService 在整個應用程式中只會有一個實例
}

prototype (原型模式)

這個作用域告訴 Spring:「不要幫我管理共用實例!每次有人需要這個 Bean 的時候,都請給我一個全新的、獨立的實例。

  • 白話解釋:想像一下你去咖啡店點一杯客製化拿鐵。每位顧客拿到的都是一杯全新的、根據自己要求製作的拿鐵,而不是和別人共用一杯。
  • 特性:每次請求,每次注入,都是全新的實例。
  • 適用場景:需要儲存狀態 (Stateful) 的物件。例如,一個購物車物件,每個使用者都應該有自己獨立的購物車,才不會把商品加到別人的車裡。
  • ⚠️ 重要提醒:對於 prototype 作用域的 Bean,Spring 在建立並交給你之後,就不再追蹤它的銷毀。你需要自行管理它的生命週期。
@Component
@Scope("prototype")
public class ShoppingCart {
    // 每次注入 ShoppingCart,都會得到一個全新的購物車實例
}

Web 應用程式專屬的作用域

在開發網站時,還有一些與使用者互動相關的作用域,這裡我們簡要介紹:

  • request:每個 HTTP 請求 (Request) 都會建立一個全新的 Bean 實例。請求結束後,實例就會被銷毀。適合用來存放單次請求的資料。
  • session:每個使用者會話 (HTTP Session) 會有一個獨立的 Bean 實例。使用者登入後開始,登出或 Session 過期後銷毀。適合存放使用者登入資訊等。
  • application:整個 Web 應用程式 (ServletContext) 的生命週期中只有一個實例。與 singleton 非常相似,但在 Web 環境中有其特定意涵。

Bean 的生命週期回呼 (Lifecycle Callbacks)

有時候,我們希望在 Bean 被 Spring 建立完成後,或在它即將被銷毀前,執行一些特定的初始化或清理工作。這就是生命週期回呼的用途。

現代化、推薦的方式:使用註解(Annotation)

這是目前業界的主流與最佳實踐,因為它讓我們的業務程式碼非常乾淨,不需要依賴任何 Spring 特定的介面,降低了程式碼與框架之間的耦合 (Coupling)

  • @PostConstruct (建構後):此註解標記的方法,會在 Bean 的建構子執行完畢、且所有依賴注入 (Dependency Injection, DI) 都完成之後被 Spring 自動呼叫。非常適合執行資源載入、初始化設定等任務。
  • @PreDestroy (銷毀前):此註解標記的方法,會在 Spring 容器準備銷毀這個 Bean 之前被呼叫。適合用來執行資源釋放、連線關閉等清理工作。(注意:此回呼對 prototype 作用域的 Bean 不會生效!)

範例:一個設定檔載入器

假設我們有一個元件,它需要在啟動時讀取一個設定檔,並在程式關閉前釋放資源。

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;

@Component
public class ConfigLoader {

    public ConfigLoader() {
        System.out.println("1. 執行建構子 (Constructor)...");
    }

    @PostConstruct
    public void init() {
        // 這個方法會在所有依賴注入完成後執行
        System.out.println("2. Bean 已初始化完成,執行 @PostConstruct 方法:開始載入設定檔...");
        // 在這裡可以加入讀取檔案、建立連線等邏輯
    }

    public void useConfig() {
        System.out.println("3. Bean 正在使用中...");
    }

    @PreDestroy
    public void cleanup() {
        // 這個方法會在應用程式關閉、Bean 被銷毀前執行
        System.out.println("4. Bean 即將被銷毀,執行 @PreDestroy 方法:開始釋放資源...");
        // 在這裡可以加入關閉檔案、中斷連線等邏輯
    }
}

相關資料來源


上一篇
Day17 - Spring Boot 依賴注入指南:從外部讀取設定 - @Value
系列文
spring boot 3 學習筆記18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言