| 屬性 | 說明 | 常用值 |
| propagation | 傳播行為 | REQUIRED(預設)、SUPPORTS、REQUIRES_NEW、NESTED... |
| isolation | 隔離級別 | READ_COMMITTED(多數 DB 預設)、REPEATABLE_READ、SERIALIZABLE |
| readOnly | 只讀優化 | true/false(預設) |
| timeout | 超時秒數 | -1(預設無超時) |
| rollbackFor | 觸發回滾的異常 | Exception.class(預設僅 RuntimeException/Error) |
| noRollbackFor | 不回滾的異常 | 特定業務異常 |
傳播行為實戰驗證
// 呼叫鏈: PayHisInfoService.update() → save() → PayInfoService.update()
@Service @Transactional(propagation=REQUIRED)
class PayHisInfoService {
@Autowired PayInfoService payInfoService;
public void update(PayHisInfo his, PayInfo info) {
save(his); // 同類內部調用,無事務傳播
payInfoService.update(info); // 跨 Bean,發生傳播
}
public void save(PayHisInfo his) { ... }
}
@Service
class PayInfoService {
@Transactional(propagation=SUPPORTS, readOnly=true)
public List findAll() { ... }
public void update(PayInfo info) { ... } // 繼承類級 REQUIRED
}
日誌證據:Participating in existing transaction 表明 PayInfoService.update 加入了外層事務,而非新建。
多線程環境下的事務隔離
若 PayHisInfoService.update 啟動新執行緒呼叫 PayInfoService.update:
- 主線程與子線程各自擁有獨立事務(ThreadLocal 綁定 Connection)。
- 子線程異常不會 自動回滾主線程事務,需手動協調(如
CompletableFuture + 異常傳遞)。
- 結論:Spring 事務管理是線程安全的,但事務上下文不跨線程傳播。
@Transactional 繼承與優先級規則
- 類級註解:父類標註
@Transactional,子類所有 public 方法 自動獲得事務增強(無需重複標註)。
- 方法級覆蓋:子類重寫父類方法並標註
@Transactional,子類屬性優先,父類配置被忽略。
- 測試基類實用模式:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
@TransactionConfiguration(transactionManager="txManager", defaultRollback=true)
@Transactional
public class BaseTest extends AbstractTransactionalJUnit4SpringContextTests { ... }
所有繼承 BaseTest 的測試類方法自動在事務中執行,測試完自動回滾,極適合整合測試。
純 AOP XML 配置替代註解
<aop:config proxy-target-class="true">
<aop:pointcut id="svcPointcut" expression="execution(public * com.xxx.service..*(..))"/>
<aop:advisor pointcut-ref="svcPointcut" advice-ref="txAdvice"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
優點:集中管理、無註解汙染、支援通配符批量匹配。缺點:重構時切點表達式易失效。
方法可見度陷阱(必讀)
❌ 以下情況 @Transactional 失效:
- 方法非
public(protected/private/package-private)。
- 類內部自調用(
this.method()),繞過代理。
- 介面方法標註但使用 CGLIB 代理 (
proxy-target-class=true) 且未實現介面。
- 註解在介面/介面方法上,但代理模式為 CGLIB(預設 Spring Boot 2.x+)。
最佳實踐:僅在具體類的 public 方法上標註,內部調用需注入自身 @Autowired SelfService self 或使用 AopContext.currentProxy()(需 exposeProxy=true)。
源碼級驗證:CglibAopProxy.intercept 流程
- 攔截器鏈構建:
advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)。
- 若鏈為空且方法 public → 直接反射調用目標(無事務)。
- 否則構建
CglibMethodInvocation,呼叫 proceed() 鏈式執行攔截器。
TransactionInterceptor 執行事務邏輯:invokeWithinTransaction → PlatformTransactionManager.getTransaction → 目標方法 → commit/rollback。
總結速記卡
- 聲明式 > 編程式;
@EnableTransactionManagement 一鍵啟用。
- 傳播行為決定事務邊界:
REQUIRED 多數場景足夠,REQUIRES_NEW 獨立提交(如日誌寫入)。
- 事務綁定線程,跨線程需自行協調。
- 父類類級註解自動遞傳,子類方法級可覆蓋。
- 只能用於 public 方法,內部調用失效。
- XML AOP 適合統一規範,註解適合精細控制,二選一避免混用。
- 上一篇文章: Java 語言核心特性與設計哲學全景解析
- 下一篇文章: Java 讀取 .properties 配置文件的多種實用方法詳解
Copyright ©
程式碼之家 All Rights Reserved