Spring 框架的事務管理是企業級應用開發的基石,它將複雜的事務邊界控制抽象為簡潔的 API,讓開發者能專注於業務邏輯。Spring 提供的事務管理主要分為程式化事務與聲明式事務兩大範式,兩者各有優劣,適用場景也截然不同。
程式化事務要求開發者在代碼中顯式調用 TransactionTemplate 或直接操作 PlatformTransactionManager 來控制事務的開始、提交與回滾。雖然這種方式提供了極高的靈活性,能精細控制事務邊界,但缺點顯而易見:
聲明式事務基於 AOP 原理,將事務管理從業務代碼中剝離,通過配置(XML 或註解)聲明事務屬性。Spring 早期使用 TransactionProxyFactoryBean,現代版本則推薦 @Transactional 註解配合 <tx:annotation-driven/>。其核心優勢在於:
無論採用哪種方式,Spring 事務體系都圍繞著兩個核心接口展開:
| 接口 | 核心職責 | 關鍵實現類 |
|---|---|---|
PlatformTransactionManager |
事務管理的核心策略接口,定義 getTransaction()、commit()、rollback() |
DataSourceTransactionManager、HibernateTransactionManager、JtaTransactionManager |
TransactionStatus |
代表當前事務的狀態,提供是否新事務、是否僅回滾、保存點管理等 | DefaultTransactionStatus |
Spring 聲明式事務的本質是 動態代理。當你在 Action/Controller 中注入 Service 時,拿到的並非原始 Service 實例,而是一個織入了事務邏輯的代理對象。為什麼代理能像原始對象一樣使用?這取決於代理模式的選擇。
類代理通過繼承目標類,重寫其方法並在其中織入事務邏輯。配置關鍵屬性:
<property name="proxyTargetClass"><value>true</value></property>
偽代碼示意:
interface IAService {
void save(ValueObject object);
}
class AService implements IAService {
public void save(ValueObject object) { /* 業務邏輯 */ }
}
// Spring 自動生成的代理類(繼承 AService)
class AProxyService extends AService {
public void save(ValueObject object) {
try {
// 啟動事務
super.save(object); // 調用父類業務邏輯
// 提交事務
} catch (Exception e) {
// 回滾事務
}
}
}
調用端:
AService a = (AService) getBean("aProxyServiceBeanName"); // 安全轉型,因為 AProxyService 是 AService 子類
a.save(obj); // 實際調用 AProxyService.save(),含事務邏輯
即使轉型為接口類型 IAService 也同樣有效,因為子類實現了父類的所有接口。Spring 使用 CGLIB 庫在運行時動態生成子類字節碼實現類代理。
接口代理要求目標類必須實現一個或多個接口。代理類實現同一接口,內部持有目標對象引用,通過委託調用。
// Spring 自動生成的代理類(實現 IAService)
class AProxyService implements IAService {
private AService aService; // 委託目標對象
public void setAService(AService aService) {
this.aService = aService;
}
public void save(ValueObject object) {
try {
// 啟動事務
aService.save(object); // 委託調用,注意這裡不是 super.save()
// 提交事務
} catch (Exception e) {
// 回滾事務
}
}
}
關鍵區別: 此時 getBean() 返回的代理對象 不能 強轉為 AService 類型(會拋 ClassCastException),因為代理類與目標類沒有繼承關係,只共享接口。必須轉型為接口類型:
IAService a = (IAService) getBean("aProxyServiceBeanName"); // 必須轉接口類型
a.save(obj); // 調用代理的 save()
Spring 使用 JDK 原生 java.lang.reflect.Proxy 實現接口代理,這也是 Spring 預設的代理策略(當 proxyTargetClass=false 或目標類實現接口時)。
@Transactional 僅對 public 方法生效,內部方法調用不會觸發事務(因繞過代理)理解代理機制不僅有助於排查事務不生效等疑難雜症,更能讓你在架構設計時做出明智的技術決策。Spring 事務抽象的優雅在於:將「什麼時候開啟事務」這一橫切關注點,通過代理模式無縫織入業務流程,實現了關注點的完美分離。