一、@EnableTransactionManagement工作原理
開啟Spring事務本質上就是增加了一個Advisor,但我們使用@EnableTransactionManagement注解來開啟Spring事務是,該注解代理的功能就是向Spring容器中添加了兩個Bean:
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
AutoProxyRegistrar主要的作用是向Spring容器中注冊了一個InfrastructureAdvisorAutoProxyCreator的Bean。而InfrastructureAdvisorAutoProxyCreator繼承了AbstractAdvisorAutoProxyCreator,所以這個類的主要作用就是開啟自動代理的作用,也就是一個BeanPostProcessor,會在初始化后步驟中去尋找Advisor類型的Bean,并判斷當前某個Bean是否有匹配的Advisor,是否需要利用動態代理產生一個代理對象。
ProxyTransactionManagementConfiguration是一個配置類,它又定義了另外三個bean:
BeanFactoryTransactionAttributeSourceAdvisor :一個Advisor
AnnotationTransactionAttributeSource :相當于BeanFactoryTransactionAttributeSourceAdvisor中的Pointcut
TransactionInterceptor :相當于BeanFactoryTransactionAttributeSourceAdvisor中的Advice
AnnotationTransactionAttributeSource就是用來判斷某個類上是否存在@Transactional注解,或者判斷某個方法上是否存在@Transactional注解的。
TransactionInterceptor就是代理邏輯,當某個類中存在@Transactional注解時,到時就產生一個代理對象作為Bean,代理對象在執行某個方法時,最終就會進入到TransactionInterceptor的invoke()方法。
基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
項目地址:https://github.com/YunaiV/ruoyi-vue-pro
視頻教程:https://doc.iocoder.cn/video/
二、Spring事務基本執行原理
一個Bean在執行Bean的創建生命周期時,會經過InfrastructureAdvisorAutoProxyCreator的初始化后的方法,會判斷當前Bean對象是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,匹配邏輯為判斷該Bean的類上是否存在@Transactional注解,或者類中的某個方法上是否存在@Transactional注解,如果存在則表示該Bean需要進行動態代理產生一個代理對象作為Bean對象。
該代理對象在執行某個方法時,會再次判斷當前執行的方法是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配則執行該Advisor中的TransactionInterceptor的invoke()方法,執行基本流程為:
利用所配置的PlatformTransactionManager事務管理器新建一個數據庫連接
修改數據庫連接的autocommit為false
執行MethodInvocation.proceed()方法,簡單理解就是執行業務方法,其中就會執行sql
如果沒有拋異常,則提交
如果拋了異常,則回滾
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
項目地址:https://github.com/YunaiV/yudao-cloud
視頻教程:https://doc.iocoder.cn/video/
三、Spring事務詳細執行流程
Spring事務執行流程圖:https://www.processon.com/view/link/5fab6edf1e0853569633cc06
四、Spring事務傳播機制
在開發過程中,經常會出現一個方法調用另外一個方法,那么這里就涉及到了多種場景,比如a()調用b():
a()和b()方法中的所有sql需要在同一個事務中嗎?
a()和b()方法需要單獨的事務嗎?
a()需要在事務中執行,b()還需要在事務中執行嗎?
等等情況…
所以,這就要求Spring事務能支持上面各種場景,這就是Spring事務傳播機制的由來。那Spring事務傳播機制是如何實現的呢?
先來看上述幾種場景中的一種情況,a()在一個事務中執行,調用b()方法時需要新開一個事務執行:
首先,代理對象執行a()方法前,先利用事務管理器新建一個數據庫連接a
將數據庫連接a的autocommit改為false
把數據庫連接a設置到ThreadLocal中
執行a()方法中的sql
執行a()方法過程中,調用了b()方法(注意用代理對象調用b()方法)
代理對象執行b()方法前,判斷出來了當前線程中已經存在一個數據庫連接a了,表示當前線程其實已經擁有一個Spring事務了,則進行掛起
掛起就是把ThreadLocal中的數據庫連接a從ThreadLocal中移除,并放入一個掛起資源對象中
掛起完成后,再次利用事務管理器新建一個數據庫連接b
將數據庫連接b的autocommit改為false
把數據庫連接b設置到ThreadLocal中
執行b()方法中的sql
b()方法正常執行完,則從ThreadLocal中拿到數據庫連接b進行提交
提交之后會恢復所掛起的數據庫連接a,這里的恢復,其實只是把在掛起資源對象中所保存的數據庫連接a再次設置到ThreadLocal中
a()方法正常執行完,則從ThreadLocal中拿到數據庫連接a進行提交
這個過程中最為核心的是:在執行某個方法時,判斷當前是否已經存在一個事務,就是判斷當前線程的ThreadLocal中是否存在一個數據庫連接對象,如果存在則表示已經存在一個事務了。
五、Spring事務傳播機制分類
其中,以非事務方式運行,表示以非Spring事務運行,表示在執行這個方法時,Spring事務管理器不會去建立數據庫連接,執行sql時,由Mybatis或JdbcTemplate自己來建立數據庫連接來執行sql。
案例分析
情況1
@Component publicclassUserService{ @Autowired privateUserServiceuserService; @Transactional publicvoidtest(){ //test方法中的sql userService.a(); } @Transactional publicvoida(){ //a方法中的sql } }
默認情況下傳播機制為REQUIRED,表示當前如果沒有事務則新建一個事務,如果有事務則在當前事務中執行。
所以上面這種情況的執行流程如下:
新建一個數據庫連接conn
設置conn的autocommit為false
執行test方法中的sql
執行a方法中的sql
執行conn的commit()方法進行提交
情況2
假如是這種情況:
@Component publicclassUserService{ @Autowired privateUserServiceuserService; @Transactional publicvoidtest(){ //test方法中的sql userService.a(); intresult=100/0; } @Transactional publicvoida(){ //a方法中的sql } }
所以上面這種情況的執行流程如下:
新建一個數據庫連接conn
設置conn的autocommit為false
執行test方法中的sql
執行a方法中的sql
拋出異常
執行conn的rollback()方法進行回滾,所以兩個方法中的sql都會回滾掉
情況3
假如是這種情況:
@Component publicclassUserService{ @Autowired privateUserServiceuserService; @Transactional publicvoidtest(){ //test方法中的sql userService.a(); } @Transactional publicvoida(){ //a方法中的sql intresult=100/0; } }
所以上面這種情況的執行流程如下:
新建一個數據庫連接conn
設置conn的autocommit為false
執行test方法中的sql
執行a方法中的sql
拋出異常
執行conn的rollback()方法進行回滾,所以兩個方法中的sql都會回滾掉
情況4
如果是這種情況:
@Component publicclassUserService{ @Autowired privateUserServiceuserService; @Transactional publicvoidtest(){ //test方法中的sql userService.a(); } @Transactional(propagation=Propagation.REQUIRES_NEW) publicvoida(){ //a方法中的sql intresult=100/0; } }
所以上面這種情況的執行流程如下:
新建一個數據庫連接conn
設置conn的autocommit為false
執行test方法中的sql
又新建一個數據庫連接conn2
執行a方法中的sql
拋出異常
執行conn2的rollback()方法進行回滾
繼續拋異常,對于test()方法而言,它會接收到一個異常,然后拋出
執行conn的rollback()方法進行回滾,最終還是兩個方法中的sql都回滾了
六、Spring事務強制回滾
正常情況下,a()調用b()方法時,如果b()方法拋了異常,但是在a()方法捕獲了,那么a()的事務還是會正常提交的,但是有的時候,我們捕獲異常可能僅僅只是不把異常信息返回給客戶端,而是為了返回一些更友好的錯誤信息,而這個時候,我們還是希望事務能回滾的,那這個時候就得告訴Spring把當前事務回滾掉,做法就是:
@Transactional publicvoidtest(){ //執行sql try{ b(); }catch(Exceptione){ //構造友好的錯誤信息返回 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } publicvoidb()throwsException{ thrownewException(); }
七、TransactionSynchronization
Spring事務有可能會提交,回滾、掛起、恢復,所以Spring事務提供了一種機制,可以讓程序員來監聽當前Spring事務所處于的狀態。
@Component publicclassUserService{ @Autowired privateJdbcTemplatejdbcTemplate; @Autowired privateUserServiceuserService; @Transactional publicvoidtest(){ TransactionSynchronizationManager.registerSynchronization(newTransactionSynchronization(){ @Override publicvoidsuspend(){ System.out.println("test被掛起了"); } @Override publicvoidresume(){ System.out.println("test被恢復了"); } @Override publicvoidbeforeCommit(booleanreadOnly){ System.out.println("test準備要提交了"); } @Override publicvoidbeforeCompletion(){ System.out.println("test準備要提交或回滾了"); } @Override publicvoidafterCommit(){ System.out.println("test提交成功了"); } @Override publicvoidafterCompletion(intstatus){ System.out.println("test提交或回滾成功了"); } }); jdbcTemplate.execute("insertintot1values(1,1,1,1,'1')"); System.out.println("test"); userService.a(); } @Transactional(propagation=Propagation.REQUIRES_NEW) publicvoida(){ TransactionSynchronizationManager.registerSynchronization(newTransactionSynchronization(){ @Override publicvoidsuspend(){ System.out.println("a被掛起了"); } @Override publicvoidresume(){ System.out.println("a被恢復了"); } @Override publicvoidbeforeCommit(booleanreadOnly){ System.out.println("a準備要提交了"); } @Override publicvoidbeforeCompletion(){ System.out.println("a準備要提交或回滾了"); } @Override publicvoidafterCommit(){ System.out.println("a提交成功了"); } @Override publicvoidafterCompletion(intstatus){ System.out.println("a提交或回滾成功了"); } }); jdbcTemplate.execute("insertintot1values(2,2,2,2,'2')"); System.out.println("a"); } }
審核編輯:湯梓紅
-
容器
+關注
關注
0文章
507瀏覽量
22364 -
spring
+關注
關注
0文章
340瀏覽量
14878
原文標題:淺談 Spring 事務底層原理
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
Spring事務失效的十種常見場景
什么是java spring
spring中聲明式事務實現原理猜想
淺談Spring事務的那些坑
發現一個Spring事務的巨坑bug 你必須要小心了
8個Spring事務失效的場景介紹

spring事務失效的一些場景

評論