javascript
Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现
前言
本專題大綱如下:
事務(wù)專題大綱「對于專題大綱我又做了調(diào)整哈,主要是希望專題的內(nèi)容能夠更豐富,更加詳細(xì)」,本來是想在源碼分析的文章中附帶講一講事務(wù)使用中的問題,這兩天想了想還是單獨(dú)寫一篇并作為事務(wù)專題的收尾篇,也是我Spring源碼專題的收尾篇。
本文大綱如下:
Spring事務(wù)應(yīng)用大綱在看這篇文章,以及下篇源碼分析的文章我希望你對Spring AOP以及有充分的了解,不然一些細(xì)節(jié)問題你可能看不明白,關(guān)于Spring AOP如果你能看完這三篇文章基本上就沒什么問題了
Spring官網(wǎng)閱讀(十八)AOP的核心概念
Spring中AOP相關(guān)的API及源碼解析,原來AOP是這樣子的
你知道Spring是怎么將AOP應(yīng)用到Bean的生命周期中的嗎?
編程式事務(wù)
Spring提供了兩種編程式事務(wù)管理的方法
-
使用?TransactionTemplate?或者?TransactionalOperator.
-
直接實(shí)現(xiàn)TransactionManager接口
如果是使用的是命令式編程,Spring推薦使用TransactionTemplate?來完成編程式事務(wù)管理,如果是響應(yīng)式編程,那么使用TransactionalOperator更加合適。
TransactionTemplate
使用示例(我這里直接用的官網(wǎng)提供的例子了)
public?class?SimpleService?implements?Service?{private?final?TransactionTemplate?transactionTemplate;//?使用構(gòu)造對transactionTemplate進(jìn)行初始化//?需要提供一個transactionManagerpublic?SimpleService(PlatformTransactionManager?transactionManager)?{this.transactionTemplate?=?new?TransactionTemplate(transactionManager);}public?Object?someServiceMethod()?{return?transactionTemplate.execute(new?TransactionCallback()?{public?Object?doInTransaction(TransactionStatus?status)?{//?這里實(shí)現(xiàn)自己的相關(guān)業(yè)務(wù)邏輯updateOperation1();return?resultOfUpdateOperation2();}});} }在上面的例子中,我們顯示的使用了TransactionTemplate來完成事務(wù)管理,通過實(shí)現(xiàn)TransactionCallback接口并在其doInTransaction方法中完成了我們對業(yè)務(wù)的處理。我們可以大概看下TransactionTemplate的execute方法的實(shí)現(xiàn):
?public?<T>?T?execute(TransactionCallback<T>?action)?throws?TransactionException?{Assert.state(this.transactionManager?!=?null,?"No?PlatformTransactionManager?set");if?(this.transactionManager?instanceof?CallbackPreferringPlatformTransactionManager)?{return?((CallbackPreferringPlatformTransactionManager)?this.transactionManager).execute(this,?action);}else?{//?1.通過事務(wù)管理器開啟事務(wù)TransactionStatus?status?=?this.transactionManager.getTransaction(this);T?result;try?{//?2.執(zhí)行傳入的業(yè)務(wù)邏輯result?=?action.doInTransaction(status);}catch?(RuntimeException?|?Error?ex)?{//?3.出現(xiàn)異常,進(jìn)行回滾rollbackOnException(status,?ex);throw?ex;}catch?(Throwable?ex)?{//?3.出現(xiàn)異常,進(jìn)行回滾rollbackOnException(status,?ex);throw?new?UndeclaredThrowableException(ex,?"TransactionCallback?threw?undeclared?checked?exception");}//?4.正常執(zhí)行完成的話,提交事務(wù)this.transactionManager.commit(status);return?result;}}這些方法具體的實(shí)現(xiàn)我們暫且不看,后續(xù)進(jìn)行源碼分析時都會詳細(xì)介紹,之所以將這個代碼貼出來是讓大家更好的理解TransactionTemplate的工作機(jī)制:實(shí)際上就是通過一個TransactionCallback封裝了業(yè)務(wù)邏輯,然后TransactionTemplate會在事務(wù)的上下文中調(diào)用。
在上面的例子中doInTransaction是有返回值的,而實(shí)際上有時候并不需要返回值,這種情況下,我們可以使用TransactionCallbackWithoutResult提代TransactionCallback。
transactionTemplate.execute(new?TransactionCallbackWithoutResult()?{protected?void?doInTransactionWithoutResult(TransactionStatus?status)?{updateOperation1();updateOperation2();} }); ?實(shí)際上我們還可以通過TransactionTemplate指定事務(wù)的屬性,例如隔離級別、超時時間、傳播行為等等
TransactionTemplate是線程安全的,我們可以全局配置一個TransactionTemplate,然后所有的類都共享這個TransactionTemplate。但是,如果某個類需要特殊的事務(wù)配置,例如需要定制隔離級別,那么我們就有必要去創(chuàng)建不同的TransactionTemplate。
?TransactionOperator
?TransactionOperator適用于響應(yīng)式編程的情況,這里就不做詳細(xì)介紹了
?TransactionManager
實(shí)際上TransactionTemplate內(nèi)部也是使用TransactionManager來完成事務(wù)管理的,我們之前也看過它的execute方法的實(shí)現(xiàn)了,其實(shí)內(nèi)部就是調(diào)用了TransactionManager的方法,實(shí)際上就是分為這么幾步
開啟事務(wù)
執(zhí)行業(yè)務(wù)邏輯
出現(xiàn)異常進(jìn)行回滾
正常執(zhí)行則提交事務(wù)
這里我還是直接用官網(wǎng)給出的例子
//?定義事務(wù) DefaultTransactionDefinition?def?=?new?DefaultTransactionDefinition(); def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); //?txManager,事務(wù)管理器 //?通過事務(wù)管理器開啟一個事務(wù) TransactionStatus?status?=?txManager.getTransaction(def); try?{//?完成自己的業(yè)務(wù)邏輯 } catch?(MyException?ex)?{//?出現(xiàn)異常,進(jìn)行回滾txManager.rollback(status);throw?ex; } //?正常執(zhí)行完成,提交事務(wù) txManager.commit(status);我們在后邊的源碼分析中其實(shí)重點(diǎn)分析的也就是TransactionManager的源碼。
申明式事務(wù)
在對編程式事務(wù)有一定了解之后我們會發(fā)現(xiàn),編程式事務(wù)存在下面幾個問題:
「我們的業(yè)務(wù)代碼跟事務(wù)管理的代碼混雜在一起」。
「每個需要事務(wù)管理的地方都需要寫重復(fù)的代碼」
如何解決呢?這就要用到申明式事務(wù)了,實(shí)現(xiàn)申明式事務(wù)一般有兩種方式
-
基于XML配置
-
基于注解
申明式事務(wù)事務(wù)的實(shí)現(xiàn)原理如下(圖片來源于官網(wǎng)):
實(shí)現(xiàn)原理?
「實(shí)際上就是結(jié)合了APO自動代理跟事務(wù)相關(guān)API」。通過開啟AOP自動代理并向容器中注冊了事務(wù)需要的通知(Transaction Advisor),在Transaction Advisor調(diào)用了事務(wù)相關(guān)API,其實(shí)內(nèi)部也是調(diào)用了TransactionManager的方法。
基于XML配置這種方式就不講了,筆者近兩年時間沒用過XML配置,我們主要就看看通過注解方式來實(shí)現(xiàn)申明式事務(wù)。主要涉及到兩個核心注解
@EnableTransactionManagement
@Transactional
@EnableTransactionManagement這個注解主要有兩個作用,其一是,開啟AOP自動代理,其二是,添加事務(wù)需要用到的通知(Transaction Advisor),如果你對AOP有一定了解的話那你應(yīng)該知道一個Advisor實(shí)際上就是一個綁定了切點(diǎn)(Pointcut)的通知(Advice),通過@EnableTransactionManagement這個注解導(dǎo)入的Advisor所綁定的切點(diǎn)就是通過@Transactional來定義的。
申明式事務(wù)的例子我這里就省去了,我相信沒幾個人不會用吧.....
Spring對事務(wù)的抽象
Spring事務(wù)抽象的關(guān)鍵就是事務(wù)策略的概念,事務(wù)策略是通過TransactionManager接口定義的。TransactionManager本身只是一個標(biāo)記接口,它有兩個直接子接口
ReactiveTransactionManager,這個接口主要用于在響應(yīng)式編程模型下,不是我們要討論的重點(diǎn)
PlatformTransactionManager,命令式編程模型下我們使用這個接口。
關(guān)于響應(yīng)式跟命令式編程都可以單獨(dú)寫一篇文章了,本文重點(diǎn)不是討論這兩種編程模型,可以認(rèn)為平常我們使用的都是命令式編程
?PlatformTransactionManager
PlatformTransactionManager接口定義
public?interface?PlatformTransactionManager?extends?TransactionManager?{//?開啟事務(wù)TransactionStatus?getTransaction(TransactionDefinition?definition)?throws?TransactionException;//?提交事務(wù)void?commit(TransactionStatus?status)?throws?TransactionException;//?回滾事務(wù)void?rollback(TransactionStatus?status)?throws?TransactionException; }PlatformTransactionManager是命令式編程模型下Spring事務(wù)機(jī)制的中心接口,定義了完成一個事務(wù)必須的三個步驟,也就是說定義了事務(wù)實(shí)現(xiàn)的規(guī)范
-
開啟事務(wù)
-
提交事務(wù)
-
回滾事務(wù)
通常來說,我們不會直接實(shí)現(xiàn)這個接口,而是通過繼承AbstractPlatformTransactionManager,這個類是一個抽象類,主要用作事務(wù)管理的模板,這個抽象類已經(jīng)實(shí)現(xiàn)了事務(wù)的傳播行為以及跟事務(wù)相關(guān)的同步管理。
回頭看接口中定義的三個方法,首先是開啟事務(wù)的方法,從方法簽名上來看,其作用就是通過一個TransactionDefinition來獲取一個TransactionStatus類型的對象。為了更好的理解Spring中事務(wù)的抽象我們有必要了解下這兩個接口
TransactionDefinition
接口定義如下:
public?interface?TransactionDefinition?{//?定義了7中事務(wù)的傳播機(jī)制int?PROPAGATION_REQUIRED?=?0;int?PROPAGATION_SUPPORTS?=?1;int?PROPAGATION_MANDATORY?=?2;int?PROPAGATION_REQUIRES_NEW?=?3;int?PROPAGATION_NOT_SUPPORTED?=?4;int?PROPAGATION_NEVER?=?5;int?PROPAGATION_NESTED?=?6;//?4種隔離級別,-1代表的是使用數(shù)據(jù)庫默認(rèn)的隔離級別//?比如在MySQL下,使用的就是ISOLATION_REPEATABLE_READ(可重復(fù)讀)int?ISOLATION_DEFAULT?=?-1;int?ISOLATION_READ_UNCOMMITTED?=?1;??int?ISOLATION_READ_COMMITTED?=?2;?int?ISOLATION_REPEATABLE_READ?=?4;?int?ISOLATION_SERIALIZABLE?=?8;??//?事務(wù)的超時時間,默認(rèn)不限制時間int?TIMEOUT_DEFAULT?=?-1;//?提供了對上面三個屬性的get方法default?int?getPropagationBehavior()?{return?PROPAGATION_REQUIRED;}default?int?getIsolationLevel()?{return?ISOLATION_DEFAULT;}default?int?getTimeout()?{return?TIMEOUT_DEFAULT;}//?事務(wù)是否是只讀的,默認(rèn)不是default?boolean?isReadOnly()?{return?false;}//?事務(wù)的名稱@Nullabledefault?String?getName()?{return?null;}//?返回一個只讀的TransactionDefinition//?只對屬性提供了getter方法,所有屬性都是接口中定義的默認(rèn)值static?TransactionDefinition?withDefaults()?{return?StaticTransactionDefinition.INSTANCE;}}從這個接口的名字上我們也能知道,它的主要完成了對事務(wù)定義的抽象,這些定義有些是數(shù)據(jù)庫層面本身就有的,例如隔離級別、是否只讀、超時時間、名稱。也有些是Spring賦予的,例如事務(wù)的傳播機(jī)制。Spring中一共定義了7種事務(wù)的傳播機(jī)制
-
TransactionDefinition.PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。
-
TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
-
TransactionDefinition.PROPAGATION_SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。
-
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
-
TransactionDefinition.PROPAGATION_NEVER:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。
-
TransactionDefinition.PROPAGATION_MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。
-
TransactionDefinition.PROPAGATION_NESTED:如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。
關(guān)于事務(wù)的傳播在源碼分析的文章中我會重點(diǎn)介紹,現(xiàn)在大家留個印象即可。
我們在使用申明式事務(wù)的時候,會通過@Transactional這個注解去申明某個方法需要進(jìn)行事務(wù)管理,在@Transactional中可以定義事務(wù)的屬性,這些屬性實(shí)際上就會被封裝到一個TransactionDefinition中,當(dāng)然封裝的時候肯定不是直接使用的接口,而是這個接口的一個實(shí)現(xiàn)類RuleBasedTransactionAttribute。RuleBasedTransactionAttribute,該類的繼承關(guān)系如下:
RuleBasedTransactionAttribute?
-
DefaultTransactionDefinition,實(shí)現(xiàn)了TransactionDefinition,并為其中的定義的屬性提供了默認(rèn)值
//?默認(rèn)的傳播機(jī)制為required,沒有事務(wù)新建一個事務(wù) //?有事務(wù)的話加入當(dāng)前事務(wù) private?int?propagationBehavior?=?PROPAGATION_REQUIRED;//?隔離級別跟數(shù)據(jù)庫默認(rèn)的隔離級別一直 private?int?isolationLevel?=?ISOLATION_DEFAULT;//?默認(rèn)為-1,不設(shè)置超時時間 private?int?timeout?=?TIMEOUT_DEFAULT;//?默認(rèn)不是只讀的 private?boolean?readOnly?=?false; -
TransactionAttribute,擴(kuò)展了``DefaultTransactionDefinition`,新增了兩個事務(wù)的屬性
//?用于指定事務(wù)使用的事務(wù)管理器的名稱 String?getQualifier(); //?指定在出現(xiàn)哪種異常時才進(jìn)行回滾 boolean?rollbackOn(Throwable?ex); -
DefaultTransactionAttribute,繼承了DefaultTransactionDefinition,同時實(shí)現(xiàn)了TransactionAttribute接口,定義了默認(rèn)的回滾異常
//?拋出RuntimeException/Error才進(jìn)行回滾 public?boolean?rollbackOn(Throwable?ex)?{return?(ex?instanceof?RuntimeException?||?ex?instanceof?Error); } -
RuleBasedTransactionAttribute,@Transactional注解的rollbackFor等屬性就會被封裝到這個類中,允許程序員自己定義回滾的異常,如果沒有指定回滾的異常,默認(rèn)「拋出RuntimeException/Error才進(jìn)行回滾」
TransactionStatus
這個接口主要用于描述Spring事務(wù)的狀態(tài),其繼承關(guān)系如下:
TransactionStatus?
-
TransactionExecution,這個接口也是用于描述事務(wù)的狀態(tài),TransactionStatus是在其上做的擴(kuò)展,內(nèi)部定義了以下幾個方法
//?判斷當(dāng)前事務(wù)是否是一個新的事務(wù) //?不是一個新事務(wù)的話,那么需要加入到已經(jīng)存在的事務(wù)中 boolean?isNewTransaction();//?事務(wù)是否被標(biāo)記成RollbackOnly //?如果被標(biāo)記成了RollbackOnly,意味著事務(wù)只能被回滾 void?setRollbackOnly();? boolean?isRollbackOnly();//?是否事務(wù)完成,回滾或提交都意味著事務(wù)完成了 boolean?isCompleted(); -
SavepointManager,定義了管理保存點(diǎn)(Savepoint)的方法,隔離級別為NESTED時就是通過設(shè)置回滾點(diǎn)來實(shí)現(xiàn)的,內(nèi)部定義了這么幾個方法
//?創(chuàng)建保存點(diǎn) Object?createSavepoint()?throws?TransactionException;//?回滾到指定保存點(diǎn) void?rollbackToSavepoint(Object?savepoint)?throws?TransactionException;//?移除回滾點(diǎn) void?releaseSavepoint(Object?savepoint)?throws?TransactionException; -
TransactionStatus,繼承了上面這些接口,額外提供了兩個方法
//用于判斷當(dāng)前事務(wù)是否設(shè)置了保存點(diǎn) boolean?hasSavepoint();//?這個方法復(fù)寫了父接口Flushable中的方法 //?主要用于刷新會話 //?對于Hibernate/jpa而言就是調(diào)用了其session/entityManager的flush方法 void?flush();
小總結(jié):
通過上面的分析我們會發(fā)現(xiàn),TransactionDefinition的主要作用是給出一份事務(wù)屬性的定義,然后事務(wù)管理器根據(jù)給出的定義來創(chuàng)建事務(wù),TransactionStatus主要是用來描述創(chuàng)建后的事務(wù)的狀態(tài)
?在對TransactionDefinition跟TransactionStatus有一定了解后,我們再回到PlatformTransactionManager接口本身,PlatformTransactionManager作為事務(wù)管理器的基礎(chǔ)接口只是定義管理一個事務(wù)必須的三個方法:開啟事務(wù),提交事務(wù),回滾事務(wù),接口僅僅是定義了規(guī)范而已,真正做事的還是要依賴它的實(shí)現(xiàn)類,所以我們來看看它的繼承關(guān)系
PlatformTransactionManager的實(shí)現(xiàn)類
PlatformTransactionManager?
-
AbstractPlatformTransactionManager,Spring提供的一個事務(wù)管理的基類,提供了事務(wù)管理的模板,實(shí)現(xiàn)了Spring事務(wù)管理的一個標(biāo)準(zhǔn)流程
-
判斷當(dāng)前是否已經(jīng)存在一個事務(wù)
-
應(yīng)用合適的事務(wù)傳播行為
-
在必要的時候掛起/恢復(fù)事務(wù)
-
提交時檢查事務(wù)是否被標(biāo)記成為rollback-only
-
在回滾時做適當(dāng)?shù)男薷?#xff08;是執(zhí)行真實(shí)的回滾/還是將事務(wù)標(biāo)記成rollback-only)
-
觸發(fā)注冊的同步回調(diào)
-
在AbstractPlatformTransactionManager提供了四個常見的子類,其說明如下
關(guān)于事務(wù)管理器的詳細(xì)代碼分析放到下篇文章,本文對其有個大概了解即可。
Spring中事務(wù)的同步機(jī)制
Spring中事務(wù)相關(guān)的同步機(jī)制可以分為兩類
-
資源的同步
-
行為的同步
什么是資源的同步呢?在一個事務(wù)中我們往往會一次執(zhí)行多個SQL(如果是單條的SQL實(shí)際上沒有必要開啟事務(wù)),為了保證事務(wù)所有的SQL都能夠使用一個數(shù)據(jù)庫連接,這個時候我們需要將數(shù)據(jù)庫連接跟事務(wù)進(jìn)行同步,這個時候數(shù)據(jù)庫連接就是跟這個事務(wù)同步的一個資源。
那什么又是行為的同步呢?還是以數(shù)據(jù)庫連接為例子,在事務(wù)開啟之前我們需要先獲取一個數(shù)據(jù)庫連接,同樣的在事務(wù)提交時我們需要將連接關(guān)閉(不一定是真正的關(guān)閉,如果是連接池只是歸還到連接池中),這個時候關(guān)閉連接這個行為也需要跟事務(wù)進(jìn)行同步
那么Spring是如何來管理同步的呢?同樣的,Spring也提供了一個同步管理器TransactionSynchronizationManager,這是一個抽象類,其中所有的方法都是靜態(tài)的,并且所有的方法都是圍繞它所申明的幾個靜態(tài)常量字段,如下:
//?這就是同步的資源,Spring就是使用這個完成了連接的同步 private?static?final?ThreadLocal<Map<Object,?Object>>?resources?=new?NamedThreadLocal<>("Transactional?resources");//?TransactionSynchronization完成了行為的同步 //?關(guān)于TransactionSynchronization在后文進(jìn)行分析 private?static?final?ThreadLocal<Set<TransactionSynchronization>>?synchronizations?=new?NamedThreadLocal<>("Transaction?synchronizations");//?事務(wù)的名稱 private?static?final?ThreadLocal<String>?currentTransactionName?=new?NamedThreadLocal<>("Current?transaction?name");//?事務(wù)是否被標(biāo)記成只讀 private?static?final?ThreadLocal<Boolean>?currentTransactionReadOnly?=new?NamedThreadLocal<>("Current?transaction?read-only?status");//?事物的隔離級別 private?static?final?ThreadLocal<Integer>?currentTransactionIsolationLevel?=new?NamedThreadLocal<>("Current?transaction?isolation?level");//?是否真實(shí)開啟了事務(wù)? private?static?final?ThreadLocal<Boolean>?actualTransactionActive?=new?NamedThreadLocal<>("Actual?transaction?active");可以看到所有的同步都是通過ThreadLocal實(shí)現(xiàn)的,對于ThreadLocal本文不做詳細(xì)分析,如果對ThreadLocal還不了解的同學(xué)也沒有關(guān)系,對于本文而言你只需要知道ThreadLocal能將資源跟當(dāng)前線程綁定即可,例如ThreadLocal<Map<Object, Object>> resources這個屬性就代表要將一個map綁定到當(dāng)前線程,它提供了set跟get方法,分別用于將屬性綁定到線程上以及獲取線程上綁定的屬性。
上面的幾個變量中除了synchronizations之外其余的應(yīng)該都很好理解,synchronizations中綁定的是一個TransactionSynchronization的集合,那么這個TransactionSynchronization有什么用呢?我們來看看它的接口定義
public?interface?TransactionSynchronization?extends?Flushable?{//?事務(wù)完成的狀態(tài)//?0?提交//?1?回滾//?2?異常狀態(tài),例如在事務(wù)執(zhí)行時出現(xiàn)異常,然后回滾,回滾時又出現(xiàn)異常//?就會被標(biāo)記成狀態(tài)2int?STATUS_COMMITTED?=?0;int?STATUS_ROLLED_BACK?=?1;int?STATUS_UNKNOWN?=?2;//?我們綁定的這些TransactionSynchronization需要跟事務(wù)同步//?1.如果事務(wù)掛起,我們需要將其掛起//?2.如果事務(wù)恢復(fù),我們需要將其恢復(fù)default?void?suspend()?{}default?void?resume()?{}@Overridedefault?void?flush()?{}//?在事務(wù)執(zhí)行過程中,提供的一些回調(diào)方法default?void?beforeCommit(boolean?readOnly)?{}default?void?beforeCompletion()?{}default?void?afterCommit()?{}default?void?afterCompletion(int?status)?{}}可以看到這個接口就是定義了一些方法,這些個方法可以在事務(wù)達(dá)到不同階段后執(zhí)行,可以認(rèn)為定義了事務(wù)執(zhí)行過程的一些回調(diào)行為,這就是我之前說的行為的同步。
模擬Spring事務(wù)的實(shí)現(xiàn)
本文的最后一部分希望大家模擬一下Spring事務(wù)的實(shí)現(xiàn),我們利用現(xiàn)有的AOP來實(shí)現(xiàn)事務(wù)的管理。數(shù)據(jù)庫訪問我們直接使用jdbc,在模擬之前我們先明確兩點(diǎn)
切點(diǎn)應(yīng)該如何定義?
通知要實(shí)現(xiàn)什么功能?
我們先說第一個問題,因?yàn)槭俏覀冏约耗M,所以關(guān)于切點(diǎn)的定義我們就設(shè)置的盡量簡單一些,不妨就直接指定某個包下的所有類。對于第二個問題,我們也不做的過于復(fù)雜,在方法執(zhí)行前開啟事務(wù),在方法執(zhí)行后提交事務(wù)并關(guān)閉連接,所以我們需要定義一個環(huán)繞通知。同時,我們也需要將連接跟事務(wù)同步,保證事務(wù)中的所有SQL共用一個事務(wù)是實(shí)現(xiàn)事務(wù)管理的必要條件。基于此,我們開始編寫代碼
我們只需要引入Spring相關(guān)的依賴跟JDBC相關(guān)依賴即可,該項(xiàng)目僅僅是一個Spring環(huán)境下的Java項(xiàng)目,沒有Web依賴,也不是SpringBoot項(xiàng)目,項(xiàng)目結(jié)構(gòu)如下:
POM文件:
<?xml?version="1.0"?encoding="UTF-8"?> <project?xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.dmz.framework</groupId><artifactId>mybatis</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.15</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.6.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.6.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version></dependency></dependencies> </project>配置類:
//?開啟AOP跟掃描組件即可 @EnableAspectJAutoProxy @ComponentScan("com.dmz.mybatis.tx_demo") public?class?Config?{}完成事務(wù)管理的核心類:
public?class?TransactionUtil?{public?static?final?ThreadLocal<Connection>?synchronousConnection?=new?ThreadLocal<Connection>();private?TransactionUtil()?{}public?static?Connection?startTransaction()?{Connection?connection?=?synchronousConnection.get();if?(connection?==?null)?{try?{//?這里替換成你自己的連接地址即可connection?=?DriverManager.getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8",?"root",?"123");synchronousConnection.set(connection);connection.setAutoCommit(false);}?catch?(SQLException?e)?{e.printStackTrace();}}return?connection;}public?static?int?execute(String?sql,?Object...?args)?{Connection?connection?=?startTransaction();try?(PreparedStatement?preparedStatement?=?connection.prepareStatement(sql))?{if?(args?!=?null)?{for?(int?i?=?1;?i?<?args.length?+?1;?i++)?{preparedStatement.setObject(i,?args[i?-?1]);}}return?preparedStatement.executeUpdate();}?catch?(SQLException?e)?{e.printStackTrace();}return?0;}public?static?void?commit()?{try?(Connection?connection?=?synchronousConnection.get())?{connection.commit();synchronousConnection.remove();}?catch?(SQLException?e)?{e.printStackTrace();}}public?static?void?rollback()?{try?(Connection?connection?=?synchronousConnection.get())?{connection.rollback();synchronousConnection.remove();}?catch?(SQLException?e)?{e.printStackTrace();}} }實(shí)際需要事務(wù)管理的類
@Component public?class?UserService?{public?void?saveUser()?{TransactionUtil.execute("INSERT?INTO?`test`.`user`(`id`,?`name`)?VALUES?(?,??)",?100,?"dmz");//?測試回滾//?throw?new?RuntimeException();} }切面:
@Component @Aspect public?class?TxAspect?{@Pointcut("execution(public?*?com.dmz.mybatis.tx_demo.service..*.*(..))")private?void?pointcut()?{}@Around("pointcut()")public?Object?around(JoinPoint?joinPoint)?throws?Throwable?{//?在方法執(zhí)行前開啟事務(wù)TransactionUtil.startTransaction();//?執(zhí)行業(yè)務(wù)邏輯Object?proceed?=?null;try?{ProceedingJoinPoint?method?=?(ProceedingJoinPoint)?joinPoint;proceed?=?method.proceed();}?catch?(Throwable?throwable)?{//?出現(xiàn)異常進(jìn)行回滾TransactionUtil.rollback();return?proceed;}//?方法執(zhí)行完成后提交事務(wù)TransactionUtil.commit();return?proceed;} }用于測試的主函數(shù):
public?class?Main?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?ac?=new?AnnotationConfigApplicationContext(Config.class);UserService?userService?=?ac.getBean(UserService.class);userService.saveUser();} }具體的測試過程跟測試結(jié)果我就不放了,大家把代碼拷貝過去自行測試就好了
總結(jié)
本文主要介紹了Spring中的事務(wù)相關(guān)內(nèi)容,對Spring中的事務(wù)抽象機(jī)制做了介紹,主要是為了讓大家在接下來一篇源碼文章中能減輕負(fù)擔(dān),希望大家可以根據(jù)自己理解動手模擬下Spring中事務(wù)的實(shí)現(xiàn)哦,當(dāng)你自己去實(shí)現(xiàn)的時候肯定會碰到一系列的問題,然后帶著這些問題看源碼你才能知道Spring為什么要做這些事情!
總結(jié)
以上是生活随笔為你收集整理的Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring事务专题(三)事务的基本概念
- 下一篇: 支付宝二面:Mybatis 接口 Map