一、問題復現
1.場景
2個service方法, 方法A中調用方法B。
方法A 是核心業務方法,涉及多張表數據變更,為了保持數據一致,用spring事務注解:@Transactional(rollbackFor = Exception.class)
方法B 比較耗時,為了不影響核心業務,方法B 用@Async注解,單獨開啟一個線程去異步執行。(方法B在另外一個類里邊,不能和A在同一個類)。
2.出錯原因
方法B是異步方法,導致方法A事務還沒提交時(不一定出錯,具體就看哪個線程執行的快了)方法B就執行了。
3.期望
期望方法A上的大事務commit后再執行方法B。
二、解決方案
1 // 注冊事務同步處理 2 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { 3 @Override 4 public void afterCommit() { 5 // 事務提交完畢時,觸發:funcB 6 funB(); 7 }
三、原理
提交一個事務同步處理,在事務commit之后執行,具體存放在threadLocal(線程本地變量)中,事務commit時會去threadLocal里邊取。源碼afterCommit是空的,沒有任何操作,可見是spring專門預留給大家使用的。
源碼:
TransactionSynchronizationAdapter是一個接口適配器,這樣不用實現接口的全部方法,按需Override即可。類圖如下圖所示:
TransactionSynchronizationAdapter實現了2個接口:
- TransactionSynchronization事務同步接口,
- Ordered執行優先級
我們這里就是實現了TransactionSynchronization接口的afterCommit()方法,最終在事務commit提交后執行。
關於spring事務執行過程圖:
四、總結
遇到問題后,很快就想到了處理方式,因為我提前儲備了相關知識:
1.spring事務系列(具體在第三章 事務源碼,里邊有鏈接)
2.@Async實現異步
3.threadLocal線程本地變量