控制反轉 (IoC) 與依賴注入 (DI) 是 Spring 的靈魂。本文從設計哲學 (好萊塢原則) 切入,系統講解 IoC 容器啟動流程、三種注入方式 (構造器/Setter/介面) 代碼實戰、XML 與註解 (@Configuration/@Bean/@Autowired) 雙軌配置、Bean 作用域與生命週期,助你建立完整知識體系。
"Don't call us, we'll call you." (別找我們,我們會找你)
new 被調用者 → 高耦合、難測試、難替換實現。@ComponentScan) / JavaConfig (@Configuration)。BeanDefinitionRegistry (Map<String, BeanDefinition>)。@Autowired 解析依賴 → 遞歸 getBean 獲取依賴。@PostConstruct、InitializingBean.afterPropertiesSet()、init-method。BeanPostProcessor 後置處理器織入切面 (如 @Transactional 代理)。@PreDestroy、DisposableBean.destroy()、destroy-method。getBean(name) 返回完全初始化的 Bean (或代理)。// XML
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userDao"/>
<constructor-arg value="defaultRole"/>
</bean>
// JavaConfig (Spring 4.3+ 單構造子可省略 @Autowired)
@Component
public class UserService {
private final UserDao userDao;
private final String defaultRole;
public UserService(UserDao userDao, @Value("${default.role}") String defaultRole) {
this.userDao = userDao;
this.defaultRole = defaultRole;
}
}
// XML
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
<property name="defaultRole" value="guest"/>
</bean>
// 類別
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) { this.userDao = userDao; } // 必須有 setter
}
定義注入介面 Injectable { void inject(Dependency d); },類實現該介面。Spring 拒絕支援,因**侵入性太強** (違背非侵入式原則)。
@Service
public class UserService {
// 欄位注入 (簡潔但隱藏依賴、難測試、不可 final) —— 不推薦生產核心邏輯
@Autowired private UserDao userDao;
// Setter 注入 (可選、可重配)
@Autowired(required = false)
public void setCache(Cache cache) { this.cache = cache; }
// 建構子注入 (推薦) + @Value 註入配置
private final String prefix;
@Autowired
public UserService(UserDao userDao, @Value("${app.prefix}") String prefix) {
this.userDao = userDao; this.prefix = prefix;
}
}
| 模式 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| XML | 集中管理、無侵入、動態修改無需重編譯 | 冗長、重構困難、類型不安全 | 舊專案、第三方庫整合、基礎設施 Bean (DataSource, TransactionManager) |
JavaConfig (@Configuration + @Bean) | 類型安全、IDE 重構支援、程式化邏輯 (條件裝配 @Conditional) | 類編譯耦合、修改需重編譯 | 新專案主流、核心業務配置、複雜條件裝配 |
組件掃描 (@ComponentScan + @Component/@Service/@Repository/@Controller) | 極簡、約定優於配置 | 啟動掃描慢、隱式依賴難追蹤、誤掃描風險 | Controller/Service/Repository 等標準分層 |
| 作用域 | 說明 | Web 環境專屬 |
|---|---|---|
| singleton (預設) | 容器單例,全局共享 | — |
| prototype | 每次 getBean 新建實例 | — |
| request | — | 單次 HTTP 請求生命週期 |
| session | — | HTTP Session 生命週期 |
| application | — | ServletContext 生命週期 |
建構子 → @Autowired (欄位/Setter) → @PostConstruct → InitializingBean.afterPropertiesSet()
→ init-method (XML) → [AOP 代理] → 就緒可用
→ @PreDestroy → DisposableBean.destroy() → destroy-method (XML) → 容器關閉
避免掃描無關類。
userService),避免衝突。