武漢++外貿(mào)網(wǎng)站建設(shè)千瓜數(shù)據(jù)
- 👏作者簡(jiǎn)介:大家好,我是愛(ài)敲代碼的小黃,獨(dú)角獸企業(yè)的Java開(kāi)發(fā)工程師,CSDN博客專(zhuān)家,阿里云專(zhuān)家博主
- 📕系列專(zhuān)欄:Java設(shè)計(jì)模式、數(shù)據(jù)結(jié)構(gòu)和算法、Kafka從入門(mén)到成神、Kafka從成神到升仙、Spring從成神到升仙系列
- 🔥如果感覺(jué)博主的文章還不錯(cuò)的話,請(qǐng)👍三連支持👍一下博主哦
- 🍂博主正在努力完成2023計(jì)劃中:以夢(mèng)為馬,揚(yáng)帆起航,2023追夢(mèng)人
- 📝聯(lián)系方式:hls1793929520,加我進(jìn)群,大家一起學(xué)習(xí),一起進(jìn)步👀
文章目錄
- Spring 事務(wù)源碼解析
- 一、引言
- 二、事務(wù)的本質(zhì)
- 1、JDBC的事務(wù)
- 2、Spring的事務(wù)
- 2.1 xml配置
- 2.2 注解配置
- 三、Spring事務(wù)源碼剖析
- 1、TransactionManager
- 1.1 獲取事務(wù)
- 1.2 提交事務(wù)
- 1.3 回滾事務(wù)
- 2、 事務(wù)AOP的實(shí)現(xiàn)
- 2.1 為什么使用AOP?
- 2.2 @EnableTransactionManagement
- 2.3 TransactionInterceptor
- 2.4 XML配置
- 四、流程圖
- 五、總結(jié)
Spring 事務(wù)源碼解析
一、引言
對(duì)于Java開(kāi)發(fā)者而言,關(guān)于 Spring
,我們一般當(dāng)做黑盒來(lái)進(jìn)行使用,不需要去打開(kāi)這個(gè)黑盒。
但隨著目前程序員行業(yè)的發(fā)展,我們有必要打開(kāi)這個(gè)黑盒,去探索其中的奧妙。
本期 Spring
源碼解析系列文章,將帶你領(lǐng)略 Spring
源碼的奧秘
本期源碼文章吸收了之前 Kafka
源碼文章的錯(cuò)誤,將不再一行一行的帶大家分析源碼,我們將一些不重要的部分當(dāng)做黑盒處理,以便我們更快、更有效的閱讀源碼。
廢話不多說(shuō),發(fā)車(chē)!
本篇目錄如下:
本文流程圖可關(guān)注公眾號(hào):愛(ài)敲代碼的小黃,回復(fù):事務(wù) 獲取
貼心的小黃為大家準(zhǔn)備的文件格式為 POS文件,方便大家直接導(dǎo)入 ProcessOn 修改使用
二、事務(wù)的本質(zhì)
??數(shù)據(jù)庫(kù)事務(wù)(Database Transaction) ,是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作,要么完全地執(zhí)行,要么完全地不執(zhí)行。
??事務(wù)處理可以確保除非事務(wù)性單元內(nèi)的所有操作都成功完成,否則不會(huì)永久更新面向數(shù)據(jù)的資源。通過(guò)將一組相關(guān)操作組合為一個(gè)要么全部成功要么全部失敗的單元,可以簡(jiǎn)化錯(cuò)誤恢復(fù)并使應(yīng)用程序更加可靠。
??一個(gè)邏輯工作單元要成為事務(wù),必須滿足所謂的 ACID
(原子性、一致性、隔離性和持久性)屬性。事務(wù)是數(shù)據(jù)庫(kù)運(yùn)行中的邏輯工作單位,由DBMS中的事務(wù)管理子系統(tǒng)負(fù)責(zé)事務(wù)的處理。
1、JDBC的事務(wù)
我們來(lái)看一下在 JDBC 中對(duì)事務(wù)的操作處理:
public class JDBCTransactionExample {public static void main(String[] args) {Connection conn = null;PreparedStatement pstmt1 = null;PreparedStatement pstmt2 = null;try {// 加載驅(qū)動(dòng)Class.forName("com.mysql.jdbc.Driver");// 獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");// 關(guān)閉自動(dòng)提交,開(kāi)啟事務(wù)conn.setAutoCommit(false);// 創(chuàng)建SQL語(yǔ)句String sql1 = "UPDATE account SET balance = balance - ? WHERE id = ?";String sql2 = "UPDATE account SET balance = balance + ? WHERE id = ?";// 創(chuàng)建PreparedStatement對(duì)象pstmt1 = conn.prepareStatement(sql1);pstmt2 = conn.prepareStatement(sql2);// 設(shè)置參數(shù)pstmt1.setDouble(1, 1000);pstmt1.setInt(2, 1);pstmt2.setDouble(1, 1000);pstmt2.setInt(2, 2);// 執(zhí)行更新操作int count1 = pstmt1.executeUpdate();int count2 = pstmt2.executeUpdate();if (count1 > 0 && count2 > 0) {System.out.println("轉(zhuǎn)賬成功");// 提交事務(wù)conn.commit();} else {System.out.println("轉(zhuǎn)賬失敗");// 回滾事務(wù)conn.rollback();}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {try {if (conn != null) {// 回滾事務(wù)conn.rollback();}} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();} finally {try {if (pstmt1 != null) {pstmt1.close();}if (pstmt2 != null) {pstmt2.close();}if (conn != null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}
}
上面的代碼,我相信大部分的人都應(yīng)該接觸過(guò),這里也就不多說(shuō)了
主要我們看幾個(gè)重點(diǎn)步驟:
- 獲取連接:
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password")
- 關(guān)閉自動(dòng)提交,開(kāi)啟事務(wù):
conn.setAutoCommit(false)
- 提交事務(wù):
conn.commit()
- 回滾事務(wù):
conn.rollback()
2、Spring的事務(wù)
我們?cè)谌粘Ia(chǎn)項(xiàng)目中,項(xiàng)目由 Controller
、Serivce
、Dao
三層進(jìn)行構(gòu)建。
我們從上圖中可以了解到:
對(duì)于 addUser
方法實(shí)際對(duì)于數(shù)據(jù)調(diào)用來(lái)說(shuō),分別調(diào)用了 insertUser()
、insertLog
方法,對(duì)數(shù)據(jù)庫(kù)的操作為兩次
我們要保證 addUser
方法是符合事務(wù)定義的。
2.1 xml配置
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd"><!-- 開(kāi)啟掃描 --><context:component-scan base-package="com.dpb.*"></context:component-scan><!-- 配置數(shù)據(jù)源 --><bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource"><property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/><property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/><property name="username" value="pms"/><property name="password" value="pms"/></bean><!-- 配置JdbcTemplate --><bean class="org.springframework.jdbc.core.JdbcTemplate" ><constructor-arg name="dataSource" ref="dataSource"/></bean><!-- Spring中,使用XML配置事務(wù)三大步驟: 1. 創(chuàng)建事務(wù)管理器 2. 配置事務(wù)方法 3. 配置AOP--><bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"><property name="dataSource" ref="dataSource"/></bean><tx:advice id="advice" transaction-manager="transactionManager"><tx:attributes><tx:method name="fun*" propagation="REQUIRED"/></tx:attributes></tx:advice><!-- aop配置 --><aop:config><aop:pointcut expression="execution(* *..service.*.*(..))" id="tx"/><aop:advisor advice-ref="advice" pointcut-ref="tx"/></aop:config>
</beans>
2.2 注解配置
首先必須要添加 @EnableTransactionManagement
注解,保證事務(wù)注解生效
@EnableTransactionManagement
public class AnnotationMain {public static void main(String[] args) {}
}
其次,在方法上添加 @Transactional
代表注解生效
@Transactional
public int insertUser(User user) {userDao.insertUser();userDao.insertLog();return 1;
}
上面的操作涉及兩個(gè)重點(diǎn):
-
事務(wù)的傳播屬性
-
事務(wù)的隔離級(jí)別
三、Spring事務(wù)源碼剖析
本次剖析源碼我們會(huì)盡量挑重點(diǎn)來(lái)講,因?yàn)槭聞?wù)源碼本身就是依靠 AOP
實(shí)現(xiàn)的,我們之前已經(jīng)很詳細(xì)的講過(guò) IOC
和 AOP
的源碼實(shí)現(xiàn)了,這次帶大家一起過(guò)一遍事務(wù)即可。
因?yàn)閺牟┲鞅旧矶?#xff0c;我感覺(jué) Spring
事務(wù)其實(shí)沒(méi)有那么的重要,面試也不???#xff0c;所以不會(huì)花大量的時(shí)間去剖析細(xì)節(jié)源碼。
1、TransactionManager
首先我們看一下這個(gè)接口的一些組成配置:
****
這里我們重點(diǎn)看 PlatformTransactionManager
的實(shí)現(xiàn),其實(shí)現(xiàn)一共三個(gè)方法:
- 獲取事務(wù):
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
- 提交事務(wù):
void commit(TransactionStatus status)
- 回滾事務(wù):
void rollback(TransactionStatus status)
我們分別看一下其如何實(shí)現(xiàn)的
1.1 獲取事務(wù)
我們想一下,在獲取事務(wù)這一階段,我們會(huì)做什么功能呢?
參考上述我們 JDBC
的步驟,這個(gè)階段應(yīng)該會(huì) 創(chuàng)建連接并且開(kāi)啟事務(wù)
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition){// PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED都需要新建事務(wù)if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {//沒(méi)有當(dāng)前事務(wù)的話,REQUIRED,REQUIRES_NEW,NESTED掛起的是空事務(wù),然后創(chuàng)建一個(gè)新事務(wù)SuspendedResourcesHolder suspendedResources = suspend(null);try {// 看這里重點(diǎn):開(kāi)始事務(wù)return startTransaction(def, transaction, debugEnabled, suspendedResources);}catch (RuntimeException | Error ex) {// 恢復(fù)掛起的事務(wù)resume(null, suspendedResources);throw ex;}}
}private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {// 是否需要新同步boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// 創(chuàng)建新的事務(wù)DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 【重點(diǎn)】開(kāi)啟事務(wù)和連接doBegin(transaction, definition);// 新同步事務(wù)的設(shè)置,針對(duì)于當(dāng)前線程的設(shè)置prepareSynchronization(status, definition);return status;
}protected void doBegin(Object transaction, TransactionDefinition definition) {// 判斷事務(wù)對(duì)象沒(méi)有數(shù)據(jù)庫(kù)連接持有器if (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {// 【重點(diǎn)】通過(guò)數(shù)據(jù)源獲取一個(gè)數(shù)據(jù)庫(kù)連接對(duì)象Connection newCon = obtainDataSource().getConnection();// 把我們的數(shù)據(jù)庫(kù)連接包裝成一個(gè)ConnectionHolder對(duì)象 然后設(shè)置到我們的txObject對(duì)象中去// 再次進(jìn)來(lái)時(shí),該 txObject 就已經(jīng)有事務(wù)配置了txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}// 【重點(diǎn)】獲取連接con = txObject.getConnectionHolder().getConnection();// 為當(dāng)前的事務(wù)設(shè)置隔離級(jí)別【數(shù)據(jù)庫(kù)的隔離級(jí)別】Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);// 設(shè)置先前隔離級(jí)別txObject.setPreviousIsolationLevel(previousIsolationLevel);// 設(shè)置是否只讀txObject.setReadOnly(definition.isReadOnly());// 關(guān)閉自動(dòng)提交if (con.getAutoCommit()) {//設(shè)置需要恢復(fù)自動(dòng)提交txObject.setMustRestoreAutoCommit(true);// 【重點(diǎn)】關(guān)閉自動(dòng)提交con.setAutoCommit(false);}// 判斷事務(wù)是否需要設(shè)置為只讀事務(wù)prepareTransactionalConnection(con, definition);// 標(biāo)記激活事務(wù)txObject.getConnectionHolder().setTransactionActive(true);// 設(shè)置事務(wù)超時(shí)時(shí)間int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// 綁定我們的數(shù)據(jù)源和連接到我們的同步管理器上,把數(shù)據(jù)源作為key,數(shù)據(jù)庫(kù)連接作為value 設(shè)置到線程變量中if (txObject.isNewConnectionHolder()) {// 將當(dāng)前獲取到的連接綁定到當(dāng)前線程TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}
}
到這里,我們的 獲取事務(wù)
接口完成了 數(shù)據(jù)庫(kù)連接的創(chuàng)建
和 關(guān)閉自動(dòng)提交(開(kāi)啟事務(wù))
,將 Connection
注冊(cè)到了緩存(resources
)當(dāng)中,便于獲取。
1.2 提交事務(wù)
public final void commit(TransactionStatus status) throws TransactionException {DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 如果在事務(wù)鏈中已經(jīng)被標(biāo)記回滾,那么不會(huì)嘗試提交事務(wù),直接回滾if (defStatus.isLocalRollbackOnly()) {// 不可預(yù)期的回滾processRollback(defStatus, false);return;}// 設(shè)置了全局回滾if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {// 可預(yù)期的回滾,可能會(huì)報(bào)異常processRollback(defStatus, true);return;}// 【重點(diǎn)】處理事務(wù)提交processCommit(defStatus);
}// 處理提交,先處理保存點(diǎn),然后處理新事務(wù),如果不是新事務(wù)不會(huì)真正提交,要等外層是新事務(wù)的才提交,
// 最后根據(jù)條件執(zhí)行數(shù)據(jù)清除,線程的私有資源解綁,重置連接自動(dòng)提交,隔離級(jí)別,是否只讀,釋放連接,恢復(fù)掛起事務(wù)等
private void processCommit(DefaultTransactionStatus status) throws TransactionException {;// 如果是獨(dú)立的事務(wù)則直接提交doCommit(status);//根據(jù)條件,完成后數(shù)據(jù)清除,和線程的私有資源解綁,重置連接自動(dòng)提交,隔離級(jí)別,是否只讀,釋放連接,恢復(fù)掛起事務(wù)等cleanupAfterCompletion(status);
}
這里比較重要的有兩個(gè)步驟:
-
doCommit
:提交事務(wù)(直接使用 JDBC 提交即可)protected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();try {// JDBC連接提交con.commit();}catch (SQLException ex) {throw new TransactionSystemException("Could not commit JDBC transaction", ex);} }
-
cleanupAfterCompletion
:數(shù)據(jù)清除,與線程中的私有資源解綁,方便釋放// 線程同步狀態(tài)清除 TransactionSynchronizationManager.clear();// 清除同步狀態(tài)【這些都是線程的緩存,使用ThreadLocal的】 public static void clear() {synchronizations.remove();currentTransactionName.remove();currentTransactionReadOnly.remove();currentTransactionIsolationLevel.remove();actualTransactionActive.remove(); } // 如果是新事務(wù)的話,進(jìn)行數(shù)據(jù)清除,線程的私有資源解綁,重置連接自動(dòng)提交,隔離級(jí)別,是否只讀,釋放連接等 doCleanupAfterCompletion(status.getTransaction());// 此方法做清除連接相關(guān)操作,比如重置自動(dòng)提交啊,只讀屬性啊,解綁數(shù)據(jù)源啊,釋放連接啊,清除鏈接持有器屬性 protected void doCleanupAfterCompletion(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;// 將數(shù)據(jù)庫(kù)連接從當(dāng)前線程中解除綁定TransactionSynchronizationManager.unbindResource(obtainDataSource());// 釋放連接Connection con = txObject.getConnectionHolder().getConnection();// 恢復(fù)數(shù)據(jù)庫(kù)連接的自動(dòng)提交屬性con.setAutoCommit(true);// 重置數(shù)據(jù)庫(kù)連接DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());// 如果當(dāng)前事務(wù)是獨(dú)立的新創(chuàng)建的事務(wù)則在事務(wù)完成時(shí)釋放數(shù)據(jù)庫(kù)連接DataSourceUtils.releaseConnection(con, this.dataSource);// 連接持有器屬性清除txObject.getConnectionHolder().clear();}
這就是我們提交事務(wù)的操作了,總之來(lái)說(shuō),主要就是 調(diào)用JDBC的commit提交
和 清除一系列的線程內(nèi)部數(shù)據(jù)和配置
1.3 回滾事務(wù)
public final void rollback(TransactionStatus status) throws TransactionException {DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;processRollback(defStatus, false);
}private void processRollback(DefaultTransactionStatus status, boolean unexpected) {// 回滾的擦歐洲哦doRollback(status);// 回滾完成后回調(diào)triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);// 根據(jù)事務(wù)狀態(tài)信息,完成后數(shù)據(jù)清除,和線程的私有資源解綁,重置連接自動(dòng)提交,隔離級(jí)別,是否只讀,釋放連接,恢復(fù)掛起事務(wù)等cleanupAfterCompletion(status);
}protected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();// jdbc的回滾con.rollback();
}
回滾事務(wù),簡(jiǎn)單來(lái)說(shuō) 調(diào)用JDBC的rollback
和 清除數(shù)據(jù)
2、 事務(wù)AOP的實(shí)現(xiàn)
我們來(lái)思考一下,利用 TransactionManager
重寫(xiě)一下我們第一步的 JDBC
的功能
@Autowiredprivate UserDao userDao;@Autowiredprivate PlatformTransactionManager txManager;@Autowiredprivate LogService logService;@Transactionalpublic void insertUser(User u) {// 1、創(chuàng)建事務(wù)定義DefaultTransactionDefinition definition = new DefaultTransactionDefinition();// 2、根據(jù)定義開(kāi)啟事務(wù)TransactionStatus status = txManager.getTransaction(definition);try {this.userDao.insert(u);Log log = new Log(System.currentTimeMillis() + "", System.currentTimeMillis() + "-" + u.getUserName());// this.doAddUser(u);this.logService.insertLog(log);// 3、提交事務(wù)txManager.commit(status);} catch (Exception e) {// 4、異常了,回滾事務(wù)txManager.rollback(status);throw e;}}
大家看到上述代碼及思考一下 AOP
的作用和源碼,有沒(méi)有一絲絲想法
比如我現(xiàn)在是面試官,問(wèn)你一個(gè)問(wèn)題:你怎么去設(shè)計(jì) Spring 的事務(wù)?
如果一點(diǎn)點(diǎn)想法都沒(méi)有的話,也不用著急,我們來(lái)慢慢的分析
2.1 為什么使用AOP?
我們想,如果我們要實(shí)現(xiàn)事務(wù),在每一個(gè)方法里面都需要進(jìn)行以下三個(gè)步驟:
- 獲取事務(wù)
- 提交事務(wù)
- 回滾事務(wù)
是不是顯得我們的代碼很臃腫,那么我們能不能把這三個(gè)步驟搞成一個(gè)通用化的東西
一些代碼在方法前執(zhí)行,一些代碼方法后執(zhí)行
這個(gè)時(shí)候,你是不是就想到了 AOP
(切面編程)了
當(dāng)然,Spring
中也是如此做的,利用 AOP
來(lái)對(duì)事務(wù)進(jìn)行了統(tǒng)一的包裝實(shí)現(xiàn)
2.2 @EnableTransactionManagement
我們知道了使用 AOP
技術(shù)實(shí)現(xiàn),那到底是如何實(shí)現(xiàn)的呢?
我們從 @EnableTransactionManagement
注解聊起,我們點(diǎn)進(jìn)該注解:
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
很明顯,TransactionManagementConfigurationSelector
類(lèi)是我們主要關(guān)注的內(nèi)容
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {/*** 此處是AdviceMode的作用,默認(rèn)是用代理,另外一個(gè)是ASPECTJ*/@Overrideprotected String[] selectImports(AdviceMode adviceMode) {switch (adviceMode) {case PROXY:return new String[] {AutoProxyRegistrar.class.getName(),ProxyTransactionManagementConfiguration.class.getName()};case ASPECTJ:return new String[] {determineTransactionAspectClass()};default:return null;}}
}
一共注冊(cè)了兩個(gè):
-
AutoProxyRegistrar.class
:注冊(cè)AOP處理器 -
ProxyTransactionManagementConfiguration.class
:代理事務(wù)配置,注冊(cè)事務(wù)需要用的一些類(lèi),而且Role=ROLE_INFRASTRUCTURE都是屬于內(nèi)部級(jí)別的@Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {// 【重點(diǎn)】注冊(cè)了 BeanFactoryTransactionAttributeSourceAdvisor 的 advisor// 其 advice 為 transactionInterceptorBeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource);advisor.setAdvice(transactionInterceptor);if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}}
到這里,看到
BeanFactoryTransactionAttributeSourceAdvisor
以advisor
結(jié)尾的類(lèi),AOP
的DNA
應(yīng)該動(dòng)了,其advice
為 transactionInterceptor到這里,我們思考一下,利用我們之前學(xué)習(xí)到的
AOP
的源碼,猜測(cè)其運(yùn)行邏輯:- 我們?cè)诜椒ㄉ蠈?xiě)上
@EnableTransactionManagement
注解,Spring 會(huì)注冊(cè)一個(gè)BeanFactoryTransactionAttributeSourceAdvisor
的類(lèi) - 創(chuàng)建對(duì)應(yīng)的方法
Bean
時(shí),會(huì)和AOP
一樣,利用該Advisor
類(lèi)生成對(duì)應(yīng)的代理對(duì)象 - 最終調(diào)用方法時(shí),會(huì)調(diào)用代理對(duì)象,并通過(guò)環(huán)繞增強(qiáng)來(lái)達(dá)到事務(wù)的功能
- 我們?cè)诜椒ㄉ蠈?xiě)上
2.3 TransactionInterceptor
我們從上面看到其 advice
正是 TransactionInterceptor
,那自然不用多說(shuō)
從上篇 AOP
得知,代理對(duì)象運(yùn)行時(shí),會(huì)拿到所有的 advice
并進(jìn)行排序,責(zé)任鏈遞歸運(yùn)行
所以,我們直接看 TransactionInterceptor
這個(gè)類(lèi)即可
這里先看一下 TransactionInterceptor
類(lèi)圖
然后看其源碼內(nèi)容:
這里的 invoke 方法怎么執(zhí)行到的,我就不多介紹了,看過(guò)上篇 AOP
的文章應(yīng)該都懂,不熟悉的小伙伴可以去看一下
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {@Override@Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable {// 獲取我們的代理對(duì)象的class屬性Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction.../*** 以事務(wù)的方式調(diào)用目標(biāo)方法* 在這埋了一個(gè)鉤子函數(shù) 用來(lái)回調(diào)目標(biāo)方法的*/return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);}
}@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation){// 獲取我們的事務(wù)屬性源對(duì)象TransactionAttributeSource tas = getTransactionAttributeSource();// 通過(guò)事務(wù)屬性源對(duì)象獲取到當(dāng)前方法的事務(wù)屬性信息final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);// 獲取我們配置的事務(wù)管理器對(duì)象final TransactionManager tm = determineTransactionManager(txAttr);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// 【重點(diǎn)】創(chuàng)建TransactionInfoTransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);try {// 執(zhí)行被增強(qiáng)方法,調(diào)用具體的處理邏輯【我們實(shí)際的方法】retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 異?;貪LcompleteTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//清除事務(wù)信息,恢復(fù)線程私有的老的事務(wù)信息cleanupTransactionInfo(txInfo);}//成功后提交,會(huì)進(jìn)行資源儲(chǔ)量,連接釋放,恢復(fù)掛起事務(wù)等操作commitTransactionAfterReturning(txInfo);return retVal;}
}// 創(chuàng)建連接 + 開(kāi)啟事務(wù)
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {// 獲取TransactionStatus事務(wù)狀態(tài)信息status = tm.getTransaction(txAttr);// 根據(jù)指定的屬性與status準(zhǔn)備一個(gè)TransactionInfo,return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}// 存在異常時(shí)回滾事務(wù)
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {// 進(jìn)行回滾txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}// 調(diào)用事務(wù)管理器的提交方法
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo){txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
然后…沒(méi)有然后了
事務(wù)就是這樣簡(jiǎn)單、樸實(shí)無(wú)華的實(shí)現(xiàn)了
2.4 XML配置
這里稍微講下 xml
配置的,xml
也一樣,在我們進(jìn)行 xml
文件解析的時(shí)候,將 BeanFactoryTransactionAttributeSourceAdvisor
組裝起來(lái)注冊(cè)到 BeanDefinitionMap
中
這里可以參考上篇 AOP
的解析和生成
生成后也一樣,調(diào)用的是代理方法,判斷改方法有沒(méi)有被代理,然后遞歸+責(zé)任鏈執(zhí)行 Advice
即可
沒(méi)什么困難的
四、流程圖
大家有沒(méi)有感覺(jué)事務(wù)有點(diǎn)水,就是用了 Spring AOP
的功能包裝了一下
根本的實(shí)現(xiàn)還是依靠的 JDBC
,但有一些不得不記的小知識(shí)點(diǎn):事務(wù)的傳播屬性
和 事務(wù)的隔離級(jí)別
這兩個(gè)關(guān)于配置的還是要去看一下,我們本篇就不再多敘述了,網(wǎng)上好多資料都有的
我們畫(huà)一下基本的流程圖
整個(gè)流程也相較于簡(jiǎn)單,有興趣的可以去更細(xì)的看一下
五、總結(jié)
記得校招時(shí)候,當(dāng)時(shí)對(duì) Spring 懵懂無(wú)知,轉(zhuǎn)眼間也被迫看了源碼
本文主要從 JDBC
組裝事務(wù)過(guò)度到 Spring
的事務(wù)注解,最終通過(guò) AOP
的技術(shù)去進(jìn)行切面處理
通過(guò)這篇文章,我相信,99% 的人應(yīng)該都可以理解了 Spring
事務(wù) 的來(lái)龍去脈
那么如何證明你真的理解了 Spring
事務(wù)呢,我這里出個(gè)經(jīng)典的題目,大家可以想一下:如果讓你設(shè)計(jì)Spring中事務(wù)的流程,你會(huì)如何設(shè)計(jì)?
如果你能看到這,那博主必須要給你一個(gè)大大的鼓勵(lì),謝謝你的支持!
喜歡的可以點(diǎn)個(gè)關(guān)注,后續(xù)會(huì)更新 Spring 循環(huán)依賴(lài)
的源碼文章
我是愛(ài)敲代碼的小黃,獨(dú)角獸企業(yè)的Java開(kāi)發(fā)工程師,CSDN博客專(zhuān)家,Java領(lǐng)域新星創(chuàng)作者,喜歡后端架構(gòu)和中間件源碼。