老生常談spring的事務傳播機制
spring的事務傳播機制
背景:實習期間幾次遇到事務方法,有一次本地測試時發現事務沒有回滾,就把簡單描述寫在wx上,今天來給spring事務做個自我總結。
1、why
為什麼會有事務傳播機制?
場景一:
- serviceA 方法調用瞭 serviceB 方法,但兩個方法都有事務,這個時候如果 serviceB 方法異常,是讓 serviceB 方法提交,還是兩個一起回滾。
場景二:
- serviceA 方法調用瞭 serviceB 方法,但是隻有 serviceA 方法加瞭事務,是否把 serviceB 也加入 serviceA 的事務,如果 serviceB 異常,是否回滾 serviceA 。
場景三:
- serviceA 方法調用瞭 serviceB 方法,兩者都有事務,serviceB 已經正常執行完,但 serviceA 異常,是否需要回滾 serviceB 的數據。
所以,我們需要有對應的事務傳播機制來控制事務。
2、傳播機制生效的條件
有瞭spring事務傳播機制,那這種機制存在的條件呢?我們知道,spring的事務是基於aop的,確切來說,是基於JDK動態代理的AOP,這種AOP有什麼特點呢? 它是基於類或者接口的,也就是說,當 @Transactional寫在一個方法上時,這個方法將會被spring動態代理, 生成一個動態代理類, 對原方法進行修飾增強,但是要註意!! 原先的方法的類並沒有什麼不同,並沒有事務,spring動態代理這個類生成的代理類才有事務,才有增強,也就是說,在同一個類裡面通過this.xx()調用本類的事務方法時,事務是不會生效的,因為你調用的不是代理類。
@Transactional @Override public void method1() { this.method2(); } @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void method2() { xx }
解決方案
關鍵在於獲取類的代理對象,而不是通過this去調用,所以以下方法都是基於這個關鍵點去解決的。
- 1、最簡單的,兩個事務方法放在不同的service裡面,這個比較簡單,就不給例子瞭。(推薦)
- 2、AOP上下文。spring提供瞭AOP上下文AopContext,因此通過AopContext,可以很方便的獲取到代理對象。
public class Myservice{ @Transactional @Override public void method1() { ((Myservice)AopContext.currentProxy()).method2(); } @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void method2() { xx } }
一運行,報錯瞭,因為exposeProxy默認為false,我們要暴露代理類,就要設置為true,可以在springboot啟動類上加一個註解
@EnableAspectJAutoProxy(exposeProxy = true)
- 3、ApplicationContext
public class Myservice{ @Autowired ApplicationContext context; Myservice service; @PostConstruct //初始化時調用,不加也行 private void getMyservice() { service = context.getBean(Myservice.class); } @Transactional @Override public void method1() { service.method2(); } @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void method2() { xx } }
第二和第三種的區別就在於,2是直接獲取代理類,3是通過調用getBean間接獲取代理類,總的來說,第一種是最方便的,也是最推薦的做法。
3、傳播機制類型
下面的類型都是針對於被調用方法來說的,理解起來要想象成兩個 service 方法的調用才可以。
PROPAGATION_REQUIRED (默認)
- 支持當前事務,如果當前沒有事務,則新建事務
- 如果當前存在事務,則加入當前事務,合並成一個事務
REQUIRES_NEW (一般用在子方法需要單獨事務)
- 新建事務,如果當前存在事務,則把當前事務掛起
- 這個方法會獨立提交事務,不受調用者的事務影響,父級異常,它也是正常提交
- (上面兩個類型是常用的,下面的比較少用)
NESTED
- 如果當前存在事務,它將會成為父級事務的一個子事務,方法結束後並沒有提交,隻有等父事務結束才提交
- 如果當前沒有事務,則新建事務
- 如果它異常,父級可以捕獲它的異常而不進行回滾,正常提交
- 但如果父級異常,它必然回滾,這就是和 REQUIRES_NEW 的區別
SUPPORTS
- 如果當前存在事務,則加入事務
- 如果當前不存在事務,則以非事務方式運行,這個和不寫沒區別
NOT_SUPPORTED
- 以非事務方式運行
- 如果當前存在事務,則把當前事務掛起
MANDATORY
- 如果當前存在事務,則運行在當前事務中
- 如果當前無事務,則拋出異常,也即父級方法必須有事務
NEVER
- 以非事務方式運行,如果當前存在事務,則拋出異常,即父級方法必須無事務
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Spring中propagation的7種事務配置及說明
- 解決spring AOP中自身方法調用無法應用代理的問題
- Spring詳細講解事務失效的場景
- Spring事務註解@Transactional失效的八種場景分析
- @Transactional註解:多個事務嵌套時,獨立事務處理方式