武漢++外貿(mào)網(wǎng)站建設(shè)千瓜數(shù)據(jù)
- 👏作者簡介:大家好,我是愛敲代碼的小黃,獨角獸企業(yè)的Java開發(fā)工程師,CSDN博客專家,阿里云專家博主
- 📕系列專欄:Java設(shè)計模式、數(shù)據(jù)結(jié)構(gòu)和算法、Kafka從入門到成神、Kafka從成神到升仙、Spring從成神到升仙系列
- 🔥如果感覺博主的文章還不錯的話,請👍三連支持👍一下博主哦
- 🍂博主正在努力完成2023計劃中:以夢為馬,揚帆起航,2023追夢人
- 📝聯(lián)系方式:hls1793929520,加我進群,大家一起學(xué)習(xí),一起進步👀
文章目錄
- 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的實現(xiàn)
- 2.1 為什么使用AOP?
- 2.2 @EnableTransactionManagement
- 2.3 TransactionInterceptor
- 2.4 XML配置
- 四、流程圖
- 五、總結(jié)
Spring 事務(wù)源碼解析
一、引言
對于Java開發(fā)者而言,關(guān)于 Spring
,我們一般當(dāng)做黑盒來進行使用,不需要去打開這個黑盒。
但隨著目前程序員行業(yè)的發(fā)展,我們有必要打開這個黑盒,去探索其中的奧妙。
本期 Spring
源碼解析系列文章,將帶你領(lǐng)略 Spring
源碼的奧秘
本期源碼文章吸收了之前 Kafka
源碼文章的錯誤,將不再一行一行的帶大家分析源碼,我們將一些不重要的部分當(dāng)做黑盒處理,以便我們更快、更有效的閱讀源碼。
廢話不多說,發(fā)車!
本篇目錄如下:
本文流程圖可關(guān)注公眾號:愛敲代碼的小黃,回復(fù):事務(wù) 獲取
貼心的小黃為大家準(zhǔn)備的文件格式為 POS文件,方便大家直接導(dǎo)入 ProcessOn 修改使用
二、事務(wù)的本質(zhì)
??數(shù)據(jù)庫事務(wù)(Database Transaction) ,是指作為單個邏輯工作單元執(zhí)行的一系列操作,要么完全地執(zhí)行,要么完全地不執(zhí)行。
??事務(wù)處理可以確保除非事務(wù)性單元內(nèi)的所有操作都成功完成,否則不會永久更新面向數(shù)據(jù)的資源。通過將一組相關(guān)操作組合為一個要么全部成功要么全部失敗的單元,可以簡化錯誤恢復(fù)并使應(yīng)用程序更加可靠。
??一個邏輯工作單元要成為事務(wù),必須滿足所謂的 ACID
(原子性、一致性、隔離性和持久性)屬性。事務(wù)是數(shù)據(jù)庫運行中的邏輯工作單位,由DBMS中的事務(wù)管理子系統(tǒng)負(fù)責(zé)事務(wù)的處理。
1、JDBC的事務(wù)
我們來看一下在 JDBC 中對事務(wù)的操作處理:
public class JDBCTransactionExample {public static void main(String[] args) {Connection conn = null;PreparedStatement pstmt1 = null;PreparedStatement pstmt2 = null;try {// 加載驅(qū)動Class.forName("com.mysql.jdbc.Driver");// 獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");// 關(guān)閉自動提交,開啟事務(wù)conn.setAutoCommit(false);// 創(chuàng)建SQL語句String sql1 = "UPDATE account SET balance = balance - ? WHERE id = ?";String sql2 = "UPDATE account SET balance = balance + ? WHERE id = ?";// 創(chuàng)建PreparedStatement對象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)該接觸過,這里也就不多說了
主要我們看幾個重點步驟:
- 獲取連接:
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password")
- 關(guān)閉自動提交,開啟事務(wù):
conn.setAutoCommit(false)
- 提交事務(wù):
conn.commit()
- 回滾事務(wù):
conn.rollback()
2、Spring的事務(wù)
我們在日常生產(chǎn)項目中,項目由 Controller
、Serivce
、Dao
三層進行構(gòu)建。
我們從上圖中可以了解到:
對于 addUser
方法實際對于數(shù)據(jù)調(diào)用來說,分別調(diào)用了 insertUser()
、insertLog
方法,對數(shù)據(jù)庫的操作為兩次
我們要保證 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"><!-- 開啟掃描 --><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;
}
上面的操作涉及兩個重點:
-
事務(wù)的傳播屬性
-
事務(wù)的隔離級別
三、Spring事務(wù)源碼剖析
本次剖析源碼我們會盡量挑重點來講,因為事務(wù)源碼本身就是依靠 AOP
實現(xiàn)的,我們之前已經(jīng)很詳細(xì)的講過 IOC
和 AOP
的源碼實現(xiàn)了,這次帶大家一起過一遍事務(wù)即可。
因為從博主本身而言,我感覺 Spring
事務(wù)其實沒有那么的重要,面試也不常考,所以不會花大量的時間去剖析細(xì)節(jié)源碼。
1、TransactionManager
首先我們看一下這個接口的一些組成配置:
****
這里我們重點看 PlatformTransactionManager
的實現(xiàn),其實現(xiàn)一共三個方法:
- 獲取事務(wù):
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
- 提交事務(wù):
void commit(TransactionStatus status)
- 回滾事務(wù):
void rollback(TransactionStatus status)
我們分別看一下其如何實現(xiàn)的
1.1 獲取事務(wù)
我們想一下,在獲取事務(wù)這一階段,我們會做什么功能呢?
參考上述我們 JDBC
的步驟,這個階段應(yīng)該會 創(chuàng)建連接并且開啟事務(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) {//沒有當(dāng)前事務(wù)的話,REQUIRED,REQUIRES_NEW,NESTED掛起的是空事務(wù),然后創(chuàng)建一個新事務(wù)SuspendedResourcesHolder suspendedResources = suspend(null);try {// 看這里重點:開始事務(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);// 【重點】開啟事務(wù)和連接doBegin(transaction, definition);// 新同步事務(wù)的設(shè)置,針對于當(dāng)前線程的設(shè)置prepareSynchronization(status, definition);return status;
}protected void doBegin(Object transaction, TransactionDefinition definition) {// 判斷事務(wù)對象沒有數(shù)據(jù)庫連接持有器if (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {// 【重點】通過數(shù)據(jù)源獲取一個數(shù)據(jù)庫連接對象Connection newCon = obtainDataSource().getConnection();// 把我們的數(shù)據(jù)庫連接包裝成一個ConnectionHolder對象 然后設(shè)置到我們的txObject對象中去// 再次進來時,該 txObject 就已經(jīng)有事務(wù)配置了txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}// 【重點】獲取連接con = txObject.getConnectionHolder().getConnection();// 為當(dāng)前的事務(wù)設(shè)置隔離級別【數(shù)據(jù)庫的隔離級別】Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);// 設(shè)置先前隔離級別txObject.setPreviousIsolationLevel(previousIsolationLevel);// 設(shè)置是否只讀txObject.setReadOnly(definition.isReadOnly());// 關(guān)閉自動提交if (con.getAutoCommit()) {//設(shè)置需要恢復(fù)自動提交txObject.setMustRestoreAutoCommit(true);// 【重點】關(guān)閉自動提交con.setAutoCommit(false);}// 判斷事務(wù)是否需要設(shè)置為只讀事務(wù)prepareTransactionalConnection(con, definition);// 標(biāo)記激活事務(wù)txObject.getConnectionHolder().setTransactionActive(true);// 設(shè)置事務(wù)超時時間int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// 綁定我們的數(shù)據(jù)源和連接到我們的同步管理器上,把數(shù)據(jù)源作為key,數(shù)據(jù)庫連接作為value 設(shè)置到線程變量中if (txObject.isNewConnectionHolder()) {// 將當(dāng)前獲取到的連接綁定到當(dāng)前線程TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}
}
到這里,我們的 獲取事務(wù)
接口完成了 數(shù)據(jù)庫連接的創(chuàng)建
和 關(guān)閉自動提交(開啟事務(wù))
,將 Connection
注冊到了緩存(resources
)當(dāng)中,便于獲取。
1.2 提交事務(wù)
public final void commit(TransactionStatus status) throws TransactionException {DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 如果在事務(wù)鏈中已經(jīng)被標(biāo)記回滾,那么不會嘗試提交事務(wù),直接回滾if (defStatus.isLocalRollbackOnly()) {// 不可預(yù)期的回滾processRollback(defStatus, false);return;}// 設(shè)置了全局回滾if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {// 可預(yù)期的回滾,可能會報異常processRollback(defStatus, true);return;}// 【重點】處理事務(wù)提交processCommit(defStatus);
}// 處理提交,先處理保存點,然后處理新事務(wù),如果不是新事務(wù)不會真正提交,要等外層是新事務(wù)的才提交,
// 最后根據(jù)條件執(zhí)行數(shù)據(jù)清除,線程的私有資源解綁,重置連接自動提交,隔離級別,是否只讀,釋放連接,恢復(fù)掛起事務(wù)等
private void processCommit(DefaultTransactionStatus status) throws TransactionException {;// 如果是獨立的事務(wù)則直接提交doCommit(status);//根據(jù)條件,完成后數(shù)據(jù)清除,和線程的私有資源解綁,重置連接自動提交,隔離級別,是否只讀,釋放連接,恢復(fù)掛起事務(wù)等cleanupAfterCompletion(status);
}
這里比較重要的有兩個步驟:
-
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ù)的話,進行數(shù)據(jù)清除,線程的私有資源解綁,重置連接自動提交,隔離級別,是否只讀,釋放連接等 doCleanupAfterCompletion(status.getTransaction());// 此方法做清除連接相關(guān)操作,比如重置自動提交啊,只讀屬性啊,解綁數(shù)據(jù)源啊,釋放連接啊,清除鏈接持有器屬性 protected void doCleanupAfterCompletion(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;// 將數(shù)據(jù)庫連接從當(dāng)前線程中解除綁定TransactionSynchronizationManager.unbindResource(obtainDataSource());// 釋放連接Connection con = txObject.getConnectionHolder().getConnection();// 恢復(fù)數(shù)據(jù)庫連接的自動提交屬性con.setAutoCommit(true);// 重置數(shù)據(jù)庫連接DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());// 如果當(dāng)前事務(wù)是獨立的新創(chuàng)建的事務(wù)則在事務(wù)完成時釋放數(shù)據(jù)庫連接DataSourceUtils.releaseConnection(con, this.dataSource);// 連接持有器屬性清除txObject.getConnectionHolder().clear();}
這就是我們提交事務(wù)的操作了,總之來說,主要就是 調(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ù)清除,和線程的私有資源解綁,重置連接自動提交,隔離級別,是否只讀,釋放連接,恢復(fù)掛起事務(wù)等cleanupAfterCompletion(status);
}protected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();// jdbc的回滾con.rollback();
}
回滾事務(wù),簡單來說 調(diào)用JDBC的rollback
和 清除數(shù)據(jù)
2、 事務(wù)AOP的實現(xiàn)
我們來思考一下,利用 TransactionManager
重寫一下我們第一步的 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ù)定義開啟事務(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
的作用和源碼,有沒有一絲絲想法
比如我現(xiàn)在是面試官,問你一個問題:你怎么去設(shè)計 Spring 的事務(wù)?
如果一點點想法都沒有的話,也不用著急,我們來慢慢的分析
2.1 為什么使用AOP?
我們想,如果我們要實現(xiàn)事務(wù),在每一個方法里面都需要進行以下三個步驟:
- 獲取事務(wù)
- 提交事務(wù)
- 回滾事務(wù)
是不是顯得我們的代碼很臃腫,那么我們能不能把這三個步驟搞成一個通用化的東西
一些代碼在方法前執(zhí)行,一些代碼方法后執(zhí)行
這個時候,你是不是就想到了 AOP
(切面編程)了
當(dāng)然,Spring
中也是如此做的,利用 AOP
來對事務(wù)進行了統(tǒng)一的包裝實現(xiàn)
2.2 @EnableTransactionManagement
我們知道了使用 AOP
技術(shù)實現(xiàn),那到底是如何實現(xiàn)的呢?
我們從 @EnableTransactionManagement
注解聊起,我們點進該注解:
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
很明顯,TransactionManagementConfigurationSelector
類是我們主要關(guān)注的內(nèi)容
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {/*** 此處是AdviceMode的作用,默認(rèn)是用代理,另外一個是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;}}
}
一共注冊了兩個:
-
AutoProxyRegistrar.class
:注冊AOP處理器 -
ProxyTransactionManagementConfiguration.class
:代理事務(wù)配置,注冊事務(wù)需要用的一些類,而且Role=ROLE_INFRASTRUCTURE都是屬于內(nèi)部級別的@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) {// 【重點】注冊了 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é)尾的類,AOP
的DNA
應(yīng)該動了,其advice
為 transactionInterceptor到這里,我們思考一下,利用我們之前學(xué)習(xí)到的
AOP
的源碼,猜測其運行邏輯:- 我們在方法上寫上
@EnableTransactionManagement
注解,Spring 會注冊一個BeanFactoryTransactionAttributeSourceAdvisor
的類 - 創(chuàng)建對應(yīng)的方法
Bean
時,會和AOP
一樣,利用該Advisor
類生成對應(yīng)的代理對象 - 最終調(diào)用方法時,會調(diào)用代理對象,并通過環(huán)繞增強來達(dá)到事務(wù)的功能
- 我們在方法上寫上
2.3 TransactionInterceptor
我們從上面看到其 advice
正是 TransactionInterceptor
,那自然不用多說
從上篇 AOP
得知,代理對象運行時,會拿到所有的 advice
并進行排序,責(zé)任鏈遞歸運行
所以,我們直接看 TransactionInterceptor
這個類即可
這里先看一下 TransactionInterceptor
類圖
然后看其源碼內(nèi)容:
這里的 invoke 方法怎么執(zhí)行到的,我就不多介紹了,看過上篇 AOP
的文章應(yīng)該都懂,不熟悉的小伙伴可以去看一下
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {@Override@Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable {// 獲取我們的代理對象的class屬性Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction.../*** 以事務(wù)的方式調(diào)用目標(biāo)方法* 在這埋了一個鉤子函數(shù) 用來回調(diào)目標(biāo)方法的*/return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);}
}@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation){// 獲取我們的事務(wù)屬性源對象TransactionAttributeSource tas = getTransactionAttributeSource();// 通過事務(wù)屬性源對象獲取到當(dāng)前方法的事務(wù)屬性信息final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);// 獲取我們配置的事務(wù)管理器對象final TransactionManager tm = determineTransactionManager(txAttr);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// 【重點】創(chuàng)建TransactionInfoTransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);try {// 執(zhí)行被增強方法,調(diào)用具體的處理邏輯【我們實際的方法】retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 異?;貪LcompleteTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//清除事務(wù)信息,恢復(fù)線程私有的老的事務(wù)信息cleanupTransactionInfo(txInfo);}//成功后提交,會進行資源儲量,連接釋放,恢復(fù)掛起事務(wù)等操作commitTransactionAfterReturning(txInfo);return retVal;}
}// 創(chuàng)建連接 + 開啟事務(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)備一個TransactionInfo,return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}// 存在異常時回滾事務(wù)
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {// 進行回滾txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}// 調(diào)用事務(wù)管理器的提交方法
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo){txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
然后…沒有然后了
事務(wù)就是這樣簡單、樸實無華的實現(xiàn)了
2.4 XML配置
這里稍微講下 xml
配置的,xml
也一樣,在我們進行 xml
文件解析的時候,將 BeanFactoryTransactionAttributeSourceAdvisor
組裝起來注冊到 BeanDefinitionMap
中
這里可以參考上篇 AOP
的解析和生成
生成后也一樣,調(diào)用的是代理方法,判斷改方法有沒有被代理,然后遞歸+責(zé)任鏈執(zhí)行 Advice
即可
沒什么困難的
四、流程圖
大家有沒有感覺事務(wù)有點水,就是用了 Spring AOP
的功能包裝了一下
根本的實現(xiàn)還是依靠的 JDBC
,但有一些不得不記的小知識點:事務(wù)的傳播屬性
和 事務(wù)的隔離級別
這兩個關(guān)于配置的還是要去看一下,我們本篇就不再多敘述了,網(wǎng)上好多資料都有的
我們畫一下基本的流程圖
整個流程也相較于簡單,有興趣的可以去更細(xì)的看一下
五、總結(jié)
記得校招時候,當(dāng)時對 Spring 懵懂無知,轉(zhuǎn)眼間也被迫看了源碼
本文主要從 JDBC
組裝事務(wù)過度到 Spring
的事務(wù)注解,最終通過 AOP
的技術(shù)去進行切面處理
通過這篇文章,我相信,99% 的人應(yīng)該都可以理解了 Spring
事務(wù) 的來龍去脈
那么如何證明你真的理解了 Spring
事務(wù)呢,我這里出個經(jīng)典的題目,大家可以想一下:如果讓你設(shè)計Spring中事務(wù)的流程,你會如何設(shè)計?
如果你能看到這,那博主必須要給你一個大大的鼓勵,謝謝你的支持!
喜歡的可以點個關(guān)注,后續(xù)會更新 Spring 循環(huán)依賴
的源碼文章
我是愛敲代碼的小黃,獨角獸企業(yè)的Java開發(fā)工程師,CSDN博客專家,Java領(lǐng)域新星創(chuàng)作者,喜歡后端架構(gòu)和中間件源碼。