先想像一個情境:
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
為你完成了三件核心工作:
@Autowired
的三種注入方式就像工廠組裝零件有不同的方法,@Autowired
也有三種常見的注入方式。接下來我們會一一介紹,並直接告訴你哪一種是現代 Spring 開發的最佳實踐 (Best Practice)。
我們把最好的方法放在第一個講!這是 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);
}
}
為什麼這是最佳選擇?
UserService
所有必要的零件。任何人看到這段程式碼,都能立刻明白它的依賴。final
關鍵字,可以確保這些依賴在後續不會被意外修改。這讓你的物件更加穩固、不容易出錯。new UserService(new MockUserRepository())
,傳入一個假的測試用物件。這讓測試變得非常簡單直接。提示:從 Spring 4.3 版開始,如果你的類別只有一個建構子,Spring 會自動使用它來進行注入,所以你可以省略
@Autowired
註解,讓程式碼更乾淨!
這是最直觀的用法,直接在類別的屬性 (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)**等複雜技巧,違反了物件導向的封裝原則。UserRepository
忘記加上 @Repository
註解)找不到對應的 Bean,userRepository
就會是 null
。你只有在實際執行到相關程式碼時才會發現錯誤。這種方式是將 @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;
}
// ...
}
這種方法主要適用於 可選的 (optional) 依賴,但這在現代應用程式開發中並不常見。
注入方式 | 推薦度 | 核心思想 | 適用場景 |
---|---|---|---|
建構子注入 (Constructor) | ⭐⭐⭐⭐⭐ | 這是必需品:沒有它,物件就無法正確建立。 | 所有強制性、必要的依賴 (99% 的情況)。 |
Setter 方法注入 (Setter) | ⭐⭐ | 這是選配件:物件可以沒有它,但有的話更好。 | 極少數的可選依賴。 |
屬性注入 (Field) | ⭐ | 這是黑魔法:方便,但隱藏細節且難以測試。 | 應盡量避免,僅用於非常簡單的範例程式碼。 |