面試官:@Transactional 與 @Async 可以同時使用嗎
@Transactional 能與 @Async 同時使用嗎,相信大部分人都回答不上來,畢竟這倆一起使用,這場景......真不一定有幾個人用過。
一、@Transactional 與 @Async 可以同時使用嗎
首先我們還是先寫個 demo 看看運行結果。
demo 地址:https://github.com/zuiyu-main/EncryptDemo/tree/mysql-transactional-async
代碼如下,使用注入的方式調用 TestService#test 方法。TestService#test 方法上同時使用 @Async 與 @Transactional 注解標注。
注意:需要在啟動類中加入@EnableAsync注解,不了解的可以看下上一篇 @Async 原理。
讓我們先看下運行結果。
通過上圖可以知道,方法已經在異步線程中執行,方法的異常也已經拋出,數據庫中也沒有插入對應的數據。得出結論,@Transactional 可以與 @Async 注解同時使用,且事務可以生效。
這里提前透漏一個知識點把,大家可以把斷點打在TransactionAspectSupport#invokeWithinTransaction()方法中243行位置處,判斷代碼是走245行的回滾邏輯還是258行的提交邏輯。
在或者在PlatformTransactionManager接口的 commit 或者 rollback 打上斷點,看執行哪個方法。
二、@Transactional 注解原理
上一篇文章中說過了 @Async 注解的原理,還沒看過的小伙伴可以過去看一眼哦。@Transactional 注解的工作原理也類似,都是 AOP 實現。
在哪找入口呢,@Async 注解的學習過程中,我們是從注解入手的,@Transactional 同樣也可以,如下圖所示。
所以上文中提到驗證事務是否生效的方式中我就是從這受啟發的。
不過今天我們看另一種方式,就是通過斷點查看調用棧的方式。
這種方式有個缺點,就是假如事務是沒有生效的,也就是說沒有被 spring 代理,此時是無法達到我們的預期的。
所以有的時候看事務有沒有生效,也可以通過這個方式。
首先在我們的異步方法第一行打入斷點。
通過這一個斷點,我們就可以知道兩個消息。
- 代碼1處,使用了Async異步執行,看了上篇文章@Async原理的一看就知道 @Async 注解生效了。
- 代碼2處,第一處發現Transaction字眼的地方,所以也可以說明,我們這個方法已經被 spring 的事務管理起來了,而事務的入口就在這TransactionInterceptor#invoke,在該方法內部又調用了 TransactionAspectSupport#invokeWithinTransaction 。
進入到方法內部,需要注意的是 tas.getTransactionAttribute()。如果這個 transaction attribute 是空,說明這個方法是沒有被事務管理的。
在 tas.getTransactionAttribute() 中,執行的是 AbstractFallbackTransactionAttributeSource 下的 getTransactionAttribute 方法,而在 getTransactionAttribute 方法中有一行 computeTransactionAttribute 方法,在該方法中有如下這樣一行代碼,這也是事務失效場景之一的 priviate 方法不支持事務。
事務失效場景:private 方法不支持事務。
如果你在 debug 時沒有攔住該方法,需要重啟,在程序啟動時會緩存該方法的事務屬性。
我們繼續回到 TransactionAspectSupport#invokeWithinTransaction 方法,繼續往下走。
在回滾事務的方法中,進行了判斷,這就是另一種事務失效的場景,異常不匹配。所以在使用事務注解時一定要注意。
事務失效場景:異常類型不匹配。
當我們沒有指定異常類型時,默認只對 RuntimeException 和 Error 有效。
所以,我們總結一下,與 一文搞懂@Async 原理一起閱讀更佳。
- 程序啟動之后為加了@Transactional 注解的方法緩存事務相關屬性。
- 當方法中同時使用 @Async 與 @Transactional 注解時,@Async的優先級更高,會在 @Async 異步的線程中進行事務管理,當發生異常時也是可以回滾的。
現在我們知道了 @Transactional 與 @Async 注解的原理,那么當他們兩個互不相干的方法互相調用還會生效嗎?
只驗證注入方式調用的形式。
三、@Transactional 調用 @Async
結論:test方法中事務生效,異步方法 test2 事務不生效。
異步方法 test2 上也加入事務注解
結論:test方法與 test2方法兩個獨立的事務,互不相關。
四、@Async 調用 @Transactional
結論:test 方法會因為 test2 方法執行報錯而異常終止,test2 方法事務回滾。
如果我們把 test 方法中插入數據的代碼移動到調用 test2 方法之前。
結論:test 方法中數據插入成功,test2 數據插入失敗,事務回滾。
總結
我們在上一篇文章中學習了 @Async 注解的原理,今天看了 @Transactional 注解的原理。
通過寫代碼的形式,驗證了:
- 當方法中同時使用 @Transactional 與 @Async 時,事務是可以生效的。
- @Transactional 調用 @Async 的方式,異步方法的事務是無法生效的。
- @Async 調用 @Transaction 的方式,異步方法事務是可以生效的,需要注意的是調用方也是沒有事務管理的。