Spring底層事務(wù)原理解析
一、@EnableTransactionManagement工作原理
開啟Spring事務(wù)本質(zhì)上就是增加了一個Advisor,但我們使用 @EnableTransactionManagement注解來開啟Spring事務(wù)是,該注解代理的功能就是向Spring容器中添加了兩個Bean:
(1)AutoProxyRegistrar (2)ProxyTransactionManagementConfiguration
(1)AutoProxyRegistrar
主要的作用是向Spring容器中注冊了一個InfrastructureAdvisorAutoProxyCreator的Bean。
而InfrastructureAdvisorAutoProxyCreator繼承了AbstractAdvisorAutoProxyCreator,所以這個類的主要作用就是開啟自動代理的作用,也就是一個BeanPostProcessor,會在初始化后步驟中去尋找Advisor類型的Bean,并判斷當(dāng)前某個Bean是否有匹配的Advisor,是否需要利用動態(tài)代理產(chǎn)生一個代理對象。
(2)ProxyTransactionManagementConfiguration是一個配置類,它又定義了另外三個bean:
bean | 定義 |
---|---|
BeanFactoryTransactionAttributeSourceAdvisor | 一個Advisor。 |
AnnotationTransactionAttributeSource | 相當(dāng)于BeanFactoryTransactionAttributeSourceAdvisor中的Pointcut。就是用來判斷某個類上是否存在@Transactional注解,或者判斷某個方法上是否存在@Transactional注解的。 |
TransactionInterceptor | 相當(dāng)于BeanFactoryTransactionAttributeSourceAdvisor中的Advice;就是代理邏輯,當(dāng)某個類中存在@Transactional注解時,到時就產(chǎn)生一個代理對象作為Bean,代理對象在執(zhí)行某個方法時,最終就會進入到TransactionInterceptor的invoke()方法。。 |
二、Spring事務(wù)基本執(zhí)行原理
一個Bean在執(zhí)行Bean的創(chuàng)建生命周期時,會經(jīng)過InfrastructureAdvisorAutoProxyCreator的初始化后的方法,會判斷當(dāng)前Bean對象是否BeanFactoryTransactionAttributeSourceAdvisor匹配,匹配邏輯為判斷該Bean的類上是否存在@Transactional注解,或者類中的某個方法上是否存在@Transactional注解,如果存在則表示該Bean需要進行動態(tài)代理產(chǎn)生一個代理對象作為Bean對象。
該代理對象在執(zhí)行某個方法時,會再次判斷當(dāng)前執(zhí)行的方法是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配則執(zhí)行該Advisor中的TransactionInterceptor的invoke()方法,
執(zhí)行基本流程為:
利用所配置的PlatformTransactionManager事務(wù)管理器新建一個數(shù)據(jù)庫連接修改數(shù)據(jù)庫連接的autocommit為false執(zhí)行MethodInvocation.proceed()方法,簡單理解就是執(zhí)行業(yè)務(wù)方法,其中就會執(zhí)行sql如果沒有拋異常,則提交如果拋了異常,則回滾 三、Spring事務(wù)的過程
1、數(shù)據(jù)庫:建立連接、開啟事務(wù)、進行sql操作、成功提交、失敗回滾
2、業(yè)務(wù)邏輯:準備工作(可以進行前置通知)、開啟事務(wù)、事務(wù)操作、成功提交(可以后置通知)、失敗回滾(異常通知)
spring的事務(wù)是由aop實現(xiàn)的,首先要生成具體的代理對象,然后按照aop流程執(zhí)行具體
的操作邏輯,正常情況下要通過通知來完成核心功能,但是事務(wù)部署通過通知來實現(xiàn)的,
而是通過TransactionInterceptor來實現(xiàn)的,然后調(diào)用invoke來實現(xiàn)具體的邏輯。
步驟如下:
1、先做準備工作,解析各個方法上事務(wù)相關(guān)的屬性,根據(jù)具體的屬性來判斷是否開始新事務(wù)。
2、當(dāng)需要開啟的時候獲取數(shù)據(jù)庫連接,關(guān)閉自動提交功能,開啟事務(wù)。
3、執(zhí)行具體的sql邏輯操作,在操作的過程中如果執(zhí)行失敗會通過 completeTransactionafterthrowing來完成事務(wù)的回滾操作,回滾的
具體邏輯是通過dorollback方法實現(xiàn),實現(xiàn)時也要先獲取連接對象,
然后通過連接對象進行回滾(conn.rollback)。
4、如果執(zhí)行成功,那么通過completeTransactionafterrunning來完成事務(wù)的提交操作,
具體邏輯是通過docommit方法來實現(xiàn),實現(xiàn)的時候也是先獲取連接,通過連接對象來
進行提交(conn.commit)。
5、最后事務(wù)執(zhí)行完畢需要清除事務(wù)相關(guān)的事務(wù)信息(cleanupTransactioninfo)。
四、Spring事務(wù)傳播機制
在開發(fā)過程中,經(jīng)常會出現(xiàn)一個方法調(diào)用另外一個方法,那么這里就涉及到了多種場景,比如a()調(diào)用b():
a()和b()方法中的所有sql需要在同一個事務(wù)中嗎?a()和b()方法中的所有sql需要在同一個事務(wù)中嗎?a()需要在事務(wù)中執(zhí)行,b()還需要在事務(wù)中執(zhí)行嗎?或者其他情況
這種情況下就要求Spring事務(wù)能支持上面各種場景,這就是Spring事務(wù)傳播機制的由來。
那Spring事務(wù)傳播機制是如何實現(xiàn)的呢?
先描述其中一個場景中情況,a()在一個事務(wù)中執(zhí)行,調(diào)用b()方法時需要新開一個事務(wù)執(zhí)行:
代理對象執(zhí)行a()方法前,先利用事務(wù)管理器新建一個數(shù)據(jù)庫連接a將數(shù)據(jù)庫連接a的autocommit改為false把數(shù)據(jù)庫連接a設(shè)置到ThreadLocal中執(zhí)行a()方法中的sql執(zhí)行a()方法過程中,調(diào)用了b()方法(注意用代理對象調(diào)用b()方法)a()方法正常執(zhí)行完,則從ThreadLocal中拿到數(shù)據(jù)庫連接a進行提交
關(guān)于步驟5的一些詳細解釋:
1、代理對象執(zhí)行b()方法前,判斷出來了當(dāng)前線程中已經(jīng)存在一個數(shù)據(jù)庫連接a了,表示當(dāng)前線程其實已經(jīng)擁有一個Spring事務(wù)了,則進行掛起
2、掛起就是把ThreadLocal中的數(shù)據(jù)庫連接a從ThreadLocal中移除,并放入一個掛起資源對象中
3、掛起完成后,再次利用事務(wù)管理器新建一個數(shù)據(jù)庫連接b
4、將數(shù)據(jù)庫連接b的autocommit改為false
5、把數(shù)據(jù)庫連接b設(shè)置到ThreadLocal中
6、執(zhí)行b()方法中的sql
7、b()方法正常執(zhí)行完,則從ThreadLocal中拿到數(shù)據(jù)庫連接b進行提交
8、提交之后會恢復(fù)所掛起的數(shù)據(jù)庫連接a,這里的恢復(fù),其實只是把在掛起資源對象中所保存的數(shù)據(jù)庫連接a再次設(shè)置到ThreadLocal中
過程中最為重要的是:在執(zhí)行某個方法時,判斷當(dāng)前是否已經(jīng)存在一個事務(wù),就是判斷當(dāng)前線程的ThreadLocal中是否存在一個數(shù)據(jù)庫連接對象,如果存在則表示已經(jīng)存在一個事務(wù)了。
五、Spring事務(wù)傳播機制分類
在這里面,以非事務(wù)方式運行,表示以非Spring事務(wù)運行,表示在執(zhí)行這個方法時,Spring事務(wù)管理器不會去建立數(shù)據(jù)庫連接,執(zhí)行sql時,由Mybatis或JdbcTemplate自己來建立數(shù)據(jù)庫連接來執(zhí)行sql。
(1)案例分析、情況1
默認情況下傳播機制為REQUIRED,表示當(dāng)前如果沒有事務(wù)則新建一個事務(wù),如果有事務(wù)則在當(dāng)前事務(wù)中執(zhí)行。
@Component public class UserService { @Autowired private UserService userService; @Transactional public void test() { // test方法中的sql userService.a(); } @Transactional public void a() { // a方法中的sql } }
所以情況1的執(zhí)行流程如下:
1、新建一個數(shù)據(jù)庫連接conn 2、設(shè)置conn的autocommit為false 3、執(zhí)行test方法中的sql 4、執(zhí)行a方法中的sql 5、執(zhí)行conn的commit()方法進行提交
(2)案例分析、情況2
如果是這種情況:
@Component public class UserService { @Autowired private UserService userService; @Transactional public void test() { // test方法中的sql userService.a(); int result = 100/0; } @Transactional public void a() { // a方法中的sql } }
所以情況2的執(zhí)行流程如下:
1、新建一個數(shù)據(jù)庫連接conn 2、設(shè)置conn的autocommit為false 3、執(zhí)行test方法中的sql 4、執(zhí)行a方法中的sql 5、拋出異常 6、執(zhí)行conn的rollback()方法進行回滾,所以兩個方法中的sql都會回滾掉
(3)案例分析、情況3
@Component public class UserService { @Autowired private UserService userService; @Transactional public void test() { // test方法中的sql userService.a(); } @Transactional public void a() { // a方法中的sql int result = 100/0; } }
所以情況3的執(zhí)行流程如下:
1、新建一個數(shù)據(jù)庫連接conn 2、設(shè)置conn的autocommit為false 3、執(zhí)行test方法中的sql 4、執(zhí)行a方法中的sql 5、拋出異常 6、執(zhí)行conn的rollback()方法進行回滾,所以兩個方法中的sql都會回滾掉
(4)案例分析、情況4
@Component public class UserService { @Autowired private UserService userService; @Transactional public void test() { // test方法中的sql userService.a(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void a() { // a方法中的sql int result = 100/0; } }
所以情況3的執(zhí)行流程如下:
1、新建一個數(shù)據(jù)庫連接conn 2、設(shè)置conn的autocommit為false 3、執(zhí)行test方法中的sql 4、又新建一個數(shù)據(jù)庫連接conn2 5、執(zhí)行a方法中的sql 6、拋出異常 7、執(zhí)行conn2的rollback()方法進行回滾 8、繼續(xù)拋異常,對于test()方法而言,它會接收到一個異常,然后拋出 9、執(zhí)行conn的rollback()方法進行回滾,最終還是兩個方法中的sql都回滾了
六、Spring事務(wù)強制回滾
正常情況下,a()調(diào)用b()方法時,如果b()方法拋了異常,但是a()方法捕獲了,那么a()的事務(wù)還是會正常提交的,但是有的時候,我們捕獲異??赡苤皇遣话旬惓P畔⒎祷亟o客戶端,而是為了返回一些更優(yōu)良的錯誤信息,所以在這個時候,我們還是希望事務(wù)能回滾的,那就得告訴Spring把當(dāng)前事務(wù)回滾掉,做法就是:
@Transactional public void test(){ // 執(zhí)行sql try { b(); } catch (Exception e) { // 構(gòu)造友好的錯誤信息返回 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } public void b() throws Exception { throw new Exception(); }
七、TransactionSynchronization
Spring事務(wù)有可能會提交,回滾、掛起、恢復(fù),所以Spring事務(wù)提供了一種機制,可以讓程序員來監(jiān)聽當(dāng)前Spring事務(wù)所處于的狀態(tài)。
@Component public class UserService { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private UserService userService; @Transactional public void test(){ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void suspend() { System.out.println("test被掛起"); } @Override public void resume() { System.out.println("test被恢復(fù)"); } @Override public void beforeCommit(boolean readOnly) { System.out.println("test準備要提交"); } @Override public void beforeCompletion() { System.out.println("test準備要提交或回滾"); } @Override public void afterCommit() { System.out.println("test提交成功"); } @Override public void afterCompletion(int status) { System.out.println("test提交或回滾成功"); } }); jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')"); System.out.println("test"); userService.a(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void a(){ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void suspend() { System.out.println("a被掛起"); } @Override public void resume() { System.out.println("a被恢復(fù)"); } @Override public void beforeCommit(boolean readOnly) { System.out.println("a準備提交"); } @Override public void beforeCompletion() { System.out.println("a準備提交或回滾"); } @Override public void afterCommit() { System.out.println("a提交成功"); } @Override public void afterCompletion(int status) { System.out.println("a提交或回滾成功"); } }); jdbcTemplate.execute("insert into t1 values(2,2,2,2,'2')"); System.out.println("a"); } }
到此這篇關(guān)于Spring底層事務(wù)原理的文章就介紹到這了,更多相關(guān)Spring底層事務(wù)原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java微服務(wù)分布式調(diào)度Elastic-job環(huán)境搭建及配置
Elastic-Job在配置中提供了JobEventConfiguration,支持數(shù)據(jù)庫方式配置,會在數(shù)據(jù)庫中自動創(chuàng)建JOB_EXECUTION_LOG和JOB_STATUS_TRACE_LOG兩張表以及若干索引,來記錄作業(yè)的相關(guān)信息2023-02-02Java非阻塞I/O模型之NIO相關(guān)知識總結(jié)
在了解NIO (Non-Block I/O) 非阻塞I/O模型之前,我們可以先了解一下原始的BIO(Block I/O) 阻塞I/O模型,NIO模型能夠以非阻塞的方式更好的利用服務(wù)器資源,需要的朋友可以參考下2021-05-05logback?EvaluatorFilter日志過濾器源碼解讀
這篇文章主要為大家介紹了logback?EvaluatorFilter日志過濾器源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11