Spring Core Concepts
本文整合 Spring Framework 的核心概念,依學習順序編排。
Spring 與 JakartaEE
Spring 整合了多項 JakartaEE (J2EE) 標準:
- Servlet API (JSR 340)
- WebSocket API (JSR 356)
- Concurrency Utilities (JSR 236)
- JSON Binding API (JSR 367)
- Bean Validation (JSR 303)
- JPA (JSR 338)
- JMS (JSR 914)
- 支援 Dependency Injection (JSR 330) 與 Common Annotations (JSR 250)。
Spring IoC (控制反轉)
Inversion of Control: 指的是將物件的建立與依賴管理權限,從開發者手中轉交給 Spring 容器。
控制反轉:一種設計思想,將物件的控制權轉移給第三方容器(IoC 容器)。
IoC 容器類型
BeanFactory: 基礎介面,提供進階配置機制。ApplicationContext:BeanFactory的子介面,增加 AOP 整合、訊息處理、事件發布等企業級功能。
Spring DI (依賴注入)
Dependency Injection: 實現 IoC 的一種具體技術。
- 物件不再自行建立依賴,而是由容器主動注入(控制反轉)。
- 優勢: 易於測試(注入 Mock 物件)、低耦合、配置靈活。
依賴注入:將相依的物件(由容器建立)注入到需要它的物件中。
注入方式
- Setter 注入: 透過
setXXX()方法。 - Constructor 注入: 透過建構子。
- 自動裝配 (Autowire):
byType: 根據屬性類型尋找。byName: 根據屬性名稱(Bean ID)尋找。
Java EE 6 CDI vs Spring DI
| 功能 | Java EE 6 CDI | Spring DI |
|---|---|---|
| 標準化 | Java 標準 API (CDI 規範)。 | 第三方框架。 |
| 註解 | 註解數量較少,系統較單純。 | 註解種類極多,功能強大但較複雜。 |
| 配置 | 較僵硬但易於理解。 | 靈活的 XML/Java 配置。 |
Spring Bean
- 由 Spring IoC 容器管理的所有 Java 物件。
Bean Scope (作用域)
Singleton: 全域單例。Prototype: 每次請求建立新實例。- Web 專用:
Request,Session,Application,WebSocket。
Spring Bean Lifecycle (生命週期)
四個主要階段:
- 實例化 (Instantiation)
- 屬性填充 (Populate)
- 初始化 (Initialization)
- 銷毀 (Destruction)
簡易流程圖
flowchart LR c[Constructor] a[Autowired] p[PostConstruct] i[InitializingBean] app[ApplicationRunner] cli[CommandLineRunner] c --> a --> p --> i --> app --> cli
詳細流程圖
flowchart TB
subgraph Instantiation_實例化
Constructor
end
subgraph Population_Of_Properties_屬性填充
Setter_Injection
end
subgraph BeanNameAware_設定Bean名稱
setBeanName
end
subgraph setBeanFactory_設定工廠
BeanFactoryAware
end
subgraph ApplicationContext_設定上下文
setApplicationContext
end
subgraph BeanPostProcessor_預處理
postProcessBeforeInitalization
end
subgraph PostConstruct_註解初始
init_method
end
subgraph InitializingBean_介面初始
afterPropertiesSet
end
subgraph Custom_Initialization_自定義初始
custom_init_method
end
subgraph postProcess_AfterIntialization_後處理
p_BeanPostProcessor
end
subgraph PreDestroy_預銷毀
destroy_method
end
subgraph DisposableBean_介面銷毀
destroy_method
end
subgraph Custom_Destruction_自定義銷毀
Custom_Destroy_Method
end
Instantiation_實例化 ==> Population_Of_Properties_屬性填充 ==> BeanNameAware_設定Bean名稱
BeanNameAware_設定Bean名稱 ==> setBeanFactory_設定工廠 ==> ApplicationContext_設定上下文
ApplicationContext_設定上下文 ==> BeanPostProcessor_預處理 ==> PostConstruct_註解初始 ==> InitializingBean_介面初始
InitializingBean_介面初始 ==> Custom_Initialization_自定義初始 ==> postProcess_AfterIntialization_後處理
postProcess_AfterIntialization_後處理 ==> PreDestroy_預銷毀 ==> DisposableBean_介面銷毀
DisposableBean_介面銷毀 ==> Custom_Destruction_自定義銷毀

ApplicationContextAware 介面
實作此介面的 Bean 可以在啟動時獲取
ApplicationContext的參照,用於手動獲取容器中的其他 Bean。
- Use Case (使用場景)
- 在事件監聯器中,當需要根據業務邏輯動態獲取 Bean 時 (例如避免循環依賴)。
- 在工具類別 (Utility/Helper Class) 中,透過靜態方法訪問 Spring 管理的服務。
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext appContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
appContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
checkApplicationContext();
return appContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
checkApplicationContext();
return (T) appContext.getBean(name);
}
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
checkApplicationContext();
Map beanMaps = appContext.getBeansOfType(clazz);
if (beanMaps != null && !beanMaps.isEmpty()) {
return (T) beanMaps.values().iterator().next();
} else {
return null;
}
}
private static void checkApplicationContext() {
if (appContext == null) {
throw new IllegalStateException("applicationContext has not been injected yet.");
}
}
}Spring AOP (切面導向)
透過代理模式實現功能的橫向切入(如日誌、安全性、事務),將業務邏輯與基礎設施關注點分離。
flowchart LR 0((AOP)) --> 1([Around 前置處理]) --> 2([Before]) --> 3[Target Object] --> 4([AfterReturning]) --> 5([After]) --> 6([Around 後置處理])
CGLIB 代理
Code Generation Library
CGLIB 是一個高效能的代碼生成庫。在 Spring AOP 中,當目標物件 沒有實作介面 時,Spring 會自動使用 CGLIB 建立代理物件。它透過繼承目標類別來實作代理,因此無法代理 final 類別或方法。
Transaction (事務管理)
Propagation (傳播行為)
@Transactional(propagation = Propagation.REQUIRED)
| 參數 | 描述 |
|---|---|
REQUIRED | 預設值。如果當前存在事務,則加入;否則建立一個新事務。 |
SUPPORTS | 如果當前存在事務,則加入;否則以非事務方式執行。 |
MANDATORY | 必須在事務中執行,否則拋出異常。 |
REQUIRES_NEW | 掛起當前事務,建立一個全新的獨立事務。 |
NOT_SUPPORTED | 以非事務方式執行,如果當前有事務則先掛起。 |
NEVER | 禁止在事務中執行,否則拋出異常。 |
NESTED | 如果當前有事務,則在嵌套事務中執行(子事務回滾不影響父事務,但父事務回滾會影響子事務)。 |
行為對比範例
- REQUIRED: A 方法調用 B 方法,兩者處於同一事務。B 出錯,全體回滾。
- REQUIRES_NEW: A 方法調用 B 方法,B 開啟獨立事務。B 回滾不影響 A;A 回滾不影響已提交的 B。
- NESTED: A 方法調用 B 方法,B 是 A 的子事務。B 出錯可單獨回滾(需 Catch),A 可繼續執行。
應用場景 (Use Case)
- 獨立日誌記錄: 業務失敗也需要記錄日誌,此時日誌服務應設為
REQUIRES_NEW。 - 複雜業務補償: 訂單主流程與積分贈送,若積分失敗不應導致訂單失敗,可使用
NESTED配合 Try-Catch。
@Transactional(propagation = Propagation.REQUIRED)
public void mainProcess() {
orderService.saveOrder(); // 成功
try {
logService.log(); // 設為 REQUIRES_NEW,即使 A 回滾,日誌依然存在
} catch (Exception e) {}
}WebSocket API
flowchart TB HTTP握手攔截器[HttpSessionHandshakeInterceptor] HTTP握手前[beforeHandshake] HTTP握手後[afterHandshake] WssText處理器[TextWebScoketHandler] 是否支持內容拆分[supportPartialMessages] Wss連線建立後[afterConnectionEstablished] Wss連線關閉後[afterConnectionClosed] HTTP握手攔截器 --> HTTP握手前 --> HTTP握手後 --> WssText處理器 --> 是否支持內容拆分 --> Wss連線建立後 --> Wss連線關閉後