iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
Software Development

spring boot 3 學習筆記系列 第 15

Day15 - Spring Boot 依賴注入指南:@Autowired 的三種注入方法比較

  • 分享至 

  • xImage
  •  

前言

先想像一個情境:

Spring 框架 (Spring Framework) 就像一個超級智慧工廠,這個工廠的核心是一個巨大的倉庫,我們稱之為 Spring IoC 容器 (Inversion of Control Container)。這個倉庫不生產實體的商品,而是專門生產並管理我們程式中各式各樣的「物件」,在 Spring 的世界裡,這些被管理的物件有一個專有名詞,叫做 Bean

傳統上,當你需要一個物件時,你會自己 new 一個出來。但在 Spring 的工廠裡,規則改變了:你不再需要親自生產 (new) 物件,而是向工廠登記你需要哪些物件 (例如 UserService, UserRepository),工廠會自動幫你生產好,並放在倉庫 (IoC 容器) 裡隨時待命。

這個「你不用自己建立物件,而是由工廠(容器)把物件給你」的過程,就叫做控制反轉 (Inversion of Control, IoC)。而工廠將你需要的 Bean「送」到你手上這個動作,就是依賴注入 (Dependency Injection, DI)

@Autowired:讓 Spring 自動幫你組裝

@Autowired 就是你貼在程式碼上的一張「組裝需求單」。它告訴 Spring 這座智慧工廠:「嘿!我這個零件 (Bean) 需要另一個零件才能運作,請自動幫我從倉庫裡找一個符合的,然後把它裝上來!」

簡單來說,@Autowired 為你完成了三件核心工作:

  1. 識別需求:它標示出「這個地方需要一個 Bean」。
  2. 尋找 Bean:Spring IoC 容器會掃描整個倉庫,尋找一個類型相符的 Bean。
  3. 自動注入:找到後,Spring 會自動把那個 Bean 的實例 (instance) 指派 (注入) 給被標註的變數。

@Autowired 的三種注入方式

就像工廠組裝零件有不同的方法,@Autowired 也有三種常見的注入方式。接下來我們會一一介紹,並直接告訴你哪一種是現代 Spring 開發的最佳實踐 (Best Practice)

建構子注入 (Constructor Injection) - 官方最推薦用法

我們把最好的方法放在第一個講!這是 Spring 官方以及絕大多數開發者都推崇的方式。它是將 @Autowired 加在類別的建構子 (Constructor) 上。

import org.springframework.stereotype.Service;

@Service // 將 UserService 登記到 Spring 工廠,讓它變成一個 Bean
public class UserService {

    // 宣告為 final,代表這個零件一旦裝上,就不能再更換,保證穩定性
    private final UserRepository userRepository;

    // 透過建構子告訴工廠:要生產一個 UserService,"必須" 提供一個 UserRepository
    // @Autowired // 當類別只有一個建構子時,此處的 @Autowired 可以省略
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
}

為什麼這是最佳選擇?

  1. 依賴關係一目了然:就像產品的設計藍圖,建構子清楚地列出了建立 UserService 所有必要的零件。任何人看到這段程式碼,都能立刻明白它的依賴。
  2. 保證物件的完整性:依賴在物件建立時就必須提供,並且透過 final 關鍵字,可以確保這些依賴在後續不會被意外修改。這讓你的物件更加穩固、不容易出錯。
  3. 極度有利於單元測試 (Unit Testing):你可以完全脫離 Spring 框架,輕鬆地 new UserService(new MockUserRepository()),傳入一個假的測試用物件。這讓測試變得非常簡單直接。
  4. 提早發現問題:如果出現「A 依賴 B,B 又依賴 A」這種循環依賴 (Circular Dependency) 的問題,使用建構子注入會在應用程式啟動時立刻失敗,讓你能及早發現並修復這個設計缺陷。

提示:從 Spring 4.3 版開始,如果你的類別只有一個建構子,Spring 會自動使用它來進行注入,所以你可以省略 @Autowired 註解,讓程式碼更乾淨!

屬性注入 (Field Injection) - 最方便但不建議

這是最直觀的用法,直接在類別的屬性 (Field) 上加上 @Autowired。雖然程式碼看起來最乾淨,但它隱藏了許多風險。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired // 請 Spring "偷偷地" 把 UserRepository 塞進來
    private UserRepository userRepository;

    // ...
}
  • 優點:程式碼最簡潔。
  • 缺點
    • 隱藏依賴:依賴關係被隱藏在類別內部,不看原始碼就不會知道它需要什麼。
    • 不利於測試:在單元測試中,很難為 userRepository 賦予一個測試用的假物件,需要使用**反射 (Reflection)**等複雜技巧,違反了物件導向的封裝原則。
    • 可能導致空指標異常 (NullPointerException):如果 Spring 因為某些原因(例如 UserRepository 忘記加上 @Repository 註解)找不到對應的 Bean,userRepository 就會是 null。你只有在實際執行到相關程式碼時才會發現錯誤。

Setter 方法注入 (Setter Injection)

這種方式是將 @Autowired 加在 Setter 方法 (Method) 上。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // ...
}
  • 優點:它提供了一個公開的 Setter 方法,讓你在測試時可以手動設定依賴,比屬性注入好一些。
  • 缺點
    • 依賴可變:這個 Setter 方法可以在物件建立後的任何時間被呼叫,從而改變內部的依賴。這使得物件的狀態變得不穩定,增加了程式的複雜性。
    • 無法保證依賴存在:它沒有強制要求在建立物件時就必須提供依賴。

這種方法主要適用於 可選的 (optional) 依賴,但這在現代應用程式開發中並不常見。

總結:我該如何選擇?

注入方式 推薦度 核心思想 適用場景
建構子注入 (Constructor) ⭐⭐⭐⭐⭐ 這是必需品:沒有它,物件就無法正確建立。 所有強制性、必要的依賴 (99% 的情況)。
Setter 方法注入 (Setter) ⭐⭐ 這是選配件:物件可以沒有它,但有的話更好。 極少數的可選依賴。
屬性注入 (Field) 這是黑魔法:方便,但隱藏細節且難以測試。 應盡量避免,僅用於非常簡單的範例程式碼。

相關資料來源


上一篇
Day14 - Spring Boot 一切的起點 - 深入了解 @SpringBootApplication
下一篇
Day16 - Spring Boot 依賴注入指南:用 @Primary 與 @Qualifier 解決多實作困境
系列文
spring boot 3 學習筆記16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言