1. 程式人生 > >Spring事務專題(四)Spring中事務的使用、抽象機制及模擬Spring事務實現

Spring事務專題(四)Spring中事務的使用、抽象機制及模擬Spring事務實現

Spring中事務的使用示例、屬性及使用中可能出現的問題 # 前言 本專題大綱如下: ![事務專題大綱](https://gitee.com/wx_cc347be696/blogImage/raw/master/image-20200808210340470.png) **對於專題大綱我又做了調整哈,主要是希望專題的內容能夠更豐富,更加詳細**,本來是想在原始碼分析的文章中附帶講一講事務使用中的問題,這兩天想了想還是單獨寫一篇並作為事務專題的收尾篇,也是我Spring原始碼專題的收尾篇。 本文大綱如下: ![Spring事務應用大綱 ](https://gitee.com/wx_cc347be696/blogImage/raw/master/Spring事務應用大綱.png) 在看這篇文章,以及下篇原始碼分析的文章我希望你對Spring AOP以及有充分的瞭解,不然一些細節問題你可能看不明白,關於Spring AOP如果你能看完這三篇文章基本上就沒什麼問題了 [Spring官網閱讀(十八)AOP的核心概念](http://mp.weixin.qq.com/s?__biz=MzU5ODg2Njk4OA==&mid=2247484207&idx=1&sn=3dbe4eee3e215cb43d5d480e824f319b&chksm=febcee6cc9cb677a1e5aca9fb4a38b68a4ed112bb7dbe842cd8d2d504ed4f17d78f1d40d456e#rd) [Spring中AOP相關的API及原始碼解析,原來AOP是這樣子的](http://mp.weixin.qq.com/s?__biz=MzU5ODg2Njk4OA==&mid=2247484440&idx=1&sn=75e59ccf5025dcedf6ff5c88b8b29d54&chksm=febce95bc9cb604d28cfbc6987f524fa6b60ea4f104450736ce1172ed9cceb6efac90c7329a5#rd) [你知道Spring是怎麼將AOP應用到Bean的生命週期中的嗎?](http://mp.weixin.qq.com/s?__biz=MzU5ODg2Njk4OA==&mid=2247484456&idx=1&sn=395189e7139ba306db901f1cadc7b08c&chksm=febce96bc9cb607df38f916490b5d81a57988e40b9380e79c1169a8b14d5a53f13e18423c7fa#rd) # 程式設計式事務 Spring提供了兩種程式設計式事務管理的方法 - 使用 `TransactionTemplate` 或者 `TransactionalOperator`. - 直接實現`TransactionManager`介面 如果是使用的是指令式程式設計,Spring推薦使用`TransactionTemplate` 來完成程式設計式事務管理,如果是響應式程式設計,那麼使用`TransactionalOperator`更加合適。 ### TransactionTemplate 使用示例(我這裡直接用的官網提供的例子了) ```java public class SimpleService implements Service { private final TransactionTemplate transactionTemplate; // 使用構造對transactionTemplate進行初始化 // 需要提供一個transactionManager public SimpleService(PlatformTransactionManager transactionManager) { this.transactionTemplate = new TransactionTemplate(transactionManager); } public Object someServiceMethod() { return transactionTemplate.execute(new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { // 這裡實現自己的相關業務邏輯 updateOperation1(); return resultOfUpdateOperation2(); } }); } } ``` 在上面的例子中,我們顯示的使用了`TransactionTemplate`來完成事務管理,通過實現`TransactionCallback`介面並在其`doInTransaction`方法中完成了我們對業務的處理。我們可以大概看下`TransactionTemplate`的`execute`方法的實現: ```java public T execute(TransactionCallback 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.通過事務管理器開啟事務 TransactionStatus status = this.transactionManager.getTransaction(this); T result; try { // 2.執行傳入的業務邏輯 result = action.doInTransaction(status); } catch (RuntimeException | Error ex) { // 3.出現異常,進行回滾 rollbackOnException(status, ex); throw ex; } catch (Throwable ex) { // 3.出現異常,進行回滾 rollbackOnException(status, ex); throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception"); } // 4.正常執行完成的話,提交事務 this.transactionManager.commit(status); return result; } } ``` 這些方法具體的實現我們暫且不看,後續進行原始碼分析時都會詳細介紹,之所以將這個程式碼貼出來是讓大家更好的理解`TransactionTemplate`的工作機制:實際上就是通過一個`TransactionCallback`封裝了業務邏輯,然後`TransactionTemplate`會在事務的上下文中呼叫。 在上面的例子中`doInTransaction`是有返回值的,而實際上有時候並不需要返回值,這種情況下,我們可以使用`TransactionCallbackWithoutResult`提代`TransactionCallback`。 ```java transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } }); ``` > 實際上我們還可以通過`TransactionTemplate`指定事務的屬性,例如隔離級別、超時時間、傳播行為等等 > > `TransactionTemplate`是執行緒安全的,我們可以全域性配置一個`TransactionTemplate`,然後所有的類都共享這個`TransactionTemplate`。但是,如果某個類需要特殊的事務配置,例如需要定製隔離級別,那麼我們就有必要去建立不同的`TransactionTemplate`。 ### TransactionOperator > TransactionOperator適用於響應式程式設計的情況,這裡就不做詳細介紹了 ### TransactionManager 實際上`TransactionTemplate`內部也是使用`TransactionManager`來完成事務管理的,我們之前也看過它的`execute`方法的實現了,其實內部就是呼叫了`TransactionManager`的方法,實際上就是分為這麼幾步 1. 開啟事務 2. 執行業務邏輯 3. 出現異常進行回滾 4. 正常執行則提交事務 這裡我還是直接用官網給出的例子 ```java // 定義事務 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // txManager,事務管理器 // 通過事務管理器開啟一個事務 TransactionStatus status = txManager.getTransaction(def); try { // 完成自己的業務邏輯 } catch (MyException ex) { // 出現異常,進行回滾 txManager.rollback(status); throw ex; } // 正常執行完成,提交事務 txManager.commit(status); ``` 我們在後邊的原始碼分析中其實重點分析的也就是`TransactionManager`的原始碼。 # 申明式事務 在對程式設計式事務有一定了解之後我們會發現,程式設計式事務存在下面幾個問題: 1. **我們的業務程式碼跟事務管理的程式碼混雜在一起**。 2. **每個需要事務管理的地方都需要寫重複的程式碼** 如何解決呢?這就要用到申明式事務了,實現申明式事務一般有兩種方式 - 基於XML配置 - 基於註解 `申明式事務`事務的實現原理如下(圖片來源於官網): ![實現原理](https://gitee.com/wx_cc347be696/blogImage/raw/master/image-20200803191257457.png) **實際上就是結合了APO自動代理跟事務相關API**。通過開啟AOP自動代理並向容器中註冊了事務需要的通知(Transaction Advisor),在`Transaction Advisor`呼叫了`事務相關API`,其實內部也是呼叫了`TransactionManager`的方法。 基於XML配置這種方式就不講了,筆者近兩年時間沒用過XML配置,我們主要就看看通過註解方式來實現申明式事務。主要涉及到兩個核心註解 1. `@EnableTransactionManagement` 2. `@Transactional` `@EnableTransactionManagement`這個註解主要有兩個作用,其一是,開啟AOP自動代理,其二是,新增事務需要用到的通知(Transaction Advisor),如果你對AOP有一定了解的話那你應該知道一個`Advisor`實際上就是一個綁定了切點(Pointcut)的通知(Advice),通過`@EnableTransactionManagement`這個註解匯入的`Advisor`所繫結的切點就是通過`@Transactional`來定義的。 申明式事務的例子我這裡就省去了,我相信沒幾個人不會用吧..... # Spring對事務的抽象 Spring事務抽象的關鍵就是事務策略的概念,事務策略是通過`TransactionManager`介面定義的。`TransactionManager`本身只是一個標記介面,它有兩個直接子介面 1. `ReactiveTransactionManager`,這個介面主要用於在響應式程式設計模型下,不是我們要討論的重點 2. `PlatformTransactionManager `,指令式程式設計模型下我們使用這個介面。 > 關於響應式跟指令式程式設計都可以單獨寫一篇文章了,本文重點不是討論這兩種程式設計模型,可以認為平常我們使用的都是指令式程式設計 ## PlatformTransactionManager ### PlatformTransactionManager介面定義 ```java public interface PlatformTransactionManager extends TransactionManager { // 開啟事務 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交事務 void commit(TransactionStatus status) throws TransactionException; // 回滾事務 void rollback(TransactionStatus status) throws TransactionException; } ``` `PlatformTransactionManager`是指令式程式設計模型下Spring事務機制的中心介面,定義了完成一個事務必須的三個步驟,也就是說定義了事務實現的規範 - 開啟事務 - 提交事務 - 回滾事務 通常來說,我們不會直接實現這個介面,而是通過繼承`AbstractPlatformTransactionManager`,這個類是一個抽象類,主要用作事務管理的模板,這個抽象類已經實現了事務的傳播行為以及跟事務相關的同步管理。 回頭看介面中定義的三個方法,首先是開啟事務的方法,從方法簽名上來看,其作用就是通過一個`TransactionDefinition`來獲取一個`TransactionStatus`型別的物件。為了更好的理解Spring中事務的抽象我們有必要了解下這兩個介面 ### TransactionDefinition 介面定義如下: ```java public interface TransactionDefinition { // 定義了7中事務的傳播機制 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代表的是使用資料庫預設的隔離級別 // 比如在MySQL下,使用的就是ISOLATION_REPEATABLE_READ(可重複讀) int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = 1; int ISOLATION_READ_COMMITTED = 2; int ISOLATION_REPEATABLE_READ = 4; int ISOLATION_SERIALIZABLE = 8; // 事務的超時時間,預設不限制時間 int TIMEOUT_DEFAULT = -1; // 提供了對上面三個屬性的get方法 default int getPropagationBehavior() { return PROPAGATION_REQUIRED; } default int getIsolationLevel() { return ISOLATION_DEFAULT; } default int getTimeout() { return TIMEOUT_DEFAULT; } // 事務是否是隻讀的,預設不是 default boolean isReadOnly() { return false; } // 事務的名稱 @Nullable default String getName() { return null; } // 返回一個只讀的TransactionDefinition // 只對屬性提供了getter方法,所有屬性都是介面中定義的預設值 static TransactionDefinition withDefaults() { return StaticTransactionDefinition.INSTANCE; } } ``` 從這個介面的名字上我們也能知道,它的主要完成了對事務定義的抽象,這些定義有些是資料庫層面本身就有的,例如`隔離級別、是否只讀、超時時間、名稱`。也有些是Spring賦予的,例如`事務的傳播機制`。Spring中一共定義了7種事務的傳播機制 - TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則建立一個新的事務。 - TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,如果當前存在事務,則把當前事務掛起。 - TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續執行。 - TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式執行,如果當前存在事務,則把當前事務掛起。 - TransactionDefinition.PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則丟擲異常。 - TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則丟擲異常。 - TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則建立一個事務作為當前事務的巢狀事務來執行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。 關於事務的傳播在原始碼分析的文章中我會重點介紹,現在大家留個印象即可。 我們在使用申明式事務的時候,會通過`@Transactional`這個註解去申明某個方法需要進行事務管理,在`@Transactional`中可以定義事務的屬性,這些屬性實際上就會被封裝到一個`TransactionDefinition`中,當然封裝的時候肯定不是直接使用的介面,而是這個介面的一個實現類`RuleBasedTransactionAttribute`。`RuleBasedTransactionAttribute`,該類的繼承關係如下: ![RuleBasedTransactionAttribute](https://gitee.com/wx_cc347be696/blogImage/raw/master/RuleBasedTransactionAttribute.png) - `DefaultTransactionDefinition`,實現了`TransactionDefinition`,併為其中的定義的屬性提供了預設值 ```java // 預設的傳播機制為required,沒有事務新建一個事務 // 有事務的話加入當前事務 private int propagationBehavior = PROPAGATION_REQUIRED; // 隔離級別跟資料庫預設的隔離級別一直 private int isolationLevel = ISOLATION_DEFAULT; // 預設為-1,不設定超時時間 private int timeout = TIMEOUT_DEFAULT; // 預設不是隻讀的 private boolean readOnly = false; ``` - `TransactionAttribute`,擴充套件了``DefaultTransactionDefinition`,新增了兩個事務的屬性 ```java // 用於指定事務使用的事務管理器的名稱 String getQualifier(); // 指定在出現哪種異常時才進行回滾 boolean rollbackOn(Throwable ex); ``` - `DefaultTransactionAttribute`,繼承了`DefaultTransactionDefinition`,同時實現了`TransactionAttribute`介面,定義了預設的回滾異常 ```java // 丟擲RuntimeException/Error才進行回滾 public boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); } ``` - `RuleBasedTransactionAttribute`,`@Transactional`註解的`rollbackFor`等屬性就會被封裝到這個類中,允許程式設計師自己定義回滾的異常,如果沒有指定回滾的異常,預設**丟擲RuntimeException/Error才進行回滾** ### TransactionStatus 這個介面主要用於描述Spring事務的狀態,其繼承關係如下: ![TransactionStatus](https://gitee.com/wx_cc347be696/blogImage/raw/master/TransactionStatus.png) - `TransactionExecution`,這個介面也是用於描述事務的狀態,`TransactionStatus`是在其上做的擴充套件,內部定義了以下幾個方法 ```java // 判斷當前事務是否是一個新的事務 // 不是一個新事務的話,那麼需要加入到已經存在的事務中 boolean isNewTransaction(); // 事務是否被標記成RollbackOnly // 如果被標記成了RollbackOnly,意味著事務只能被回滾 void setRollbackOnly(); boolean isRollbackOnly(); // 是否事務完成,回滾或提交都意味著事務完成了 boolean isCompleted(); ``` - `SavepointManager`,定義了管理儲存點(`Savepoint`)的方法,隔離級別為`NESTED`時就是通過設定回滾點來實現的,內部定義了這麼幾個方法 ```java // 建立儲存點 Object createSavepoint() throws TransactionException; // 回滾到指定儲存點 void rollbackToSavepoint(Object savepoint) throws TransactionException; // 移除回滾點 void releaseSavepoint(Object savepoint) throws TransactionException; ``` - `TransactionStatus`,繼承了上面這些介面,額外提供了兩個方法 ```java //用於判斷當前事務是否設定了儲存點 boolean hasSavepoint(); // 這個方法複寫了父介面Flushable中的方法 // 主要用於重新整理會話 // 對於Hibernate/jpa而言就是呼叫了其session/entityManager的flush方法 void flush(); ``` > 小總結: > > 通過上面的分析我們會發現,`TransactionDefinition`的主要作用是給出一份事務屬性的定義,然後事務管理器根據給出的定義來建立事務,`TransactionStatus`主要是用來描述建立後的事務的狀態 在對`TransactionDefinition`跟`TransactionStatus`有一定了解後,我們再回到`PlatformTransactionManager`介面本身,`PlatformTransactionManager`作為事務管理器的基礎介面只是定義管理一個事務必須的三個方法:`開啟事務`,`提交事務`,`回滾事務`,介面僅僅是定義了規範而已,真正做事的還是要依賴它的實現類,所以我們來看看它的繼承關係 ### PlatformTransactionManager的實現類 ![PlatformTransactionManager](https://gitee.com/wx_cc347be696/blogImage/raw/master/PlatformTransactionManager.png) - `AbstractPlatformTransactionManager`,Spring提供的一個事務管理的基類,提供了事務管理的模板,實現了Spring事務管理的一個標準流程 1. 判斷當前是否已經存在一個事務 2. 應用合適的事務傳播行為 3. 在必要的時候掛起/恢復事務 4. 提交時檢查事務是否被標記成為`rollback-only` 5. 在回滾時做適當的修改(是執行真實的回滾/還是將事務標記成`rollback-only`) 6. 觸發註冊的同步回撥 - 在`AbstractPlatformTransactionManager`提供了四個常見的子類,其說明如下 ![image-20200806194650050](https://gitee.com/wx_cc347be696/blogImage/raw/master/image-20200806194650050.png) 關於事務管理器的詳細程式碼分析放到下篇文章,本文對其有個大概瞭解即可。 # Spring中事務的同步機制 Spring中事務相關的同步機制可以分為兩類 - 資源的同步 - 行為的同步 什麼是資源的同步呢?在一個事務中我們往往會一次執行多個`SQL`(如果是單條的SQL實際上沒有必要開啟事務),為了保證事務所有的`SQL`都能夠使用一個數據庫連線,這個時候我們需要將資料庫連線跟事務進行同步,這個時候資料庫連線就是跟這個事務同步的一個資源。 那什麼又是行為的同步呢?還是以資料庫連線為例子,在事務開啟之前我們需要先獲取一個數據庫連線,同樣的在事務提交時我們需要將連線關閉(不一定是真正的關閉,如果是連線池只是歸還到連線池中),這個時候關閉連線這個行為也需要跟事務進行同步 那麼Spring是如何來管理同步的呢?同樣的,Spring也提供了一個同步管理器`TransactionSynchronizationManager`,這是一個抽象類,其中所有的方法都是靜態的,並且所有的方法都是圍繞它所申明的幾個靜態常量欄位,如下: ```java // 這就是同步的資源,Spring就是使用這個完成了連線的同步 private static final ThreadLocal> resources = new NamedThreadLocal<>("Transactional resources"); // TransactionSynchronization完成了行為的同步 // 關於TransactionSynchronization在後文進行分析 private static final ThreadLocal> synchronizations = new NamedThreadLocal<>("Transaction synchronizations"); // 事務的名稱 private static final ThreadLocal currentTransactionName = new NamedThreadLocal<>("Current transaction name"); // 事務是否被標記成只讀 private static final ThreadLocal currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status"); // 事物的隔離級別 private static final ThreadLocal currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level"); // 是否真實開啟了事務 private static final ThreadLocal actualTransactionActive = new NamedThreadLocal<>("Actual transaction active"); ``` 可以看到所有的同步都是通過`ThreadLocal`實現的,對於`ThreadLocal`本文不做詳細分析,如果對`ThreadLocal`還不瞭解的同學也沒有關係,對於本文而言你只需要知道`ThreadLocal`能將資源跟當前執行緒繫結即可,例如` ThreadLocal> resources`這個屬性就代表要將一個map繫結到當前執行緒,它提供了set跟get方法,分別用於將屬性繫結到執行緒上以及獲取執行緒上繫結的屬性。 上面的幾個變數中除了`synchronizations`之外其餘的應該都很好理解,`synchronizations`中繫結的是一個`TransactionSynchronization`的集合,那麼這個`TransactionSynchronization`有什麼用呢?我們來看看它的介面定義 ```java public interface TransactionSynchronization extends Flushable { // 事務完成的狀態 // 0 提交 // 1 回滾 // 2 異常狀態,例如在事務執行時出現異常,然後回滾,回滾時又出現異常 // 就會被標記成狀態2 int STATUS_COMMITTED = 0; int STATUS_ROLLED_BACK = 1; int STATUS_UNKNOWN = 2; // 我們繫結的這些TransactionSynchronization需要跟事務同步 // 1.如果事務掛起,我們需要將其掛起 // 2.如果事務恢復,我們需要將其恢復 default void suspend() { } default void resume() { } @Override default void flush() { } // 在事務執行過程中,提供的一些回撥方法 default void beforeCommit(boolean readOnly) { } default void beforeCompletion() { } default void afterCommit() { } default void afterCompletion(int status) { } } ``` 可以看到這個介面就是定義了一些方法,這些個方法可以在事務達到不同階段後執行,可以認為定義了事務執行過程的一些回撥行為,這就是我之前說的`行為的同步`。 # 模擬Spring事務的實現 本文的最後一部分希望大家模擬一下Spring事務的實現,我們利用現有的AOP來實現事務的管理。資料庫訪問我們直接使用`jdbc`,在模擬之前我們先明確兩點 1. 切點應該如何定義? 2. 通知要實現什麼功能? 我們先說第一個問題,因為是我們自己模擬,所以關於切點的定義我們就設定的儘量簡單一些,不妨就直接指定某個包下的所有類。對於第二個問題,我們也不做的過於複雜,在方法執行前開啟事務,在方法執行後提交事務並關閉連線,所以我們需要定義一個環繞通知。同時,我們也需要將連線跟事務同步,保證事務中的所有SQL共用一個事務是實現事務管理的必要條件。基於此,我們開始編寫程式碼 我們只需要引入Spring相關的依賴跟`JDBC`相關依賴即可,該專案僅僅是一個Spring環境下的Java專案,沒有Web依賴,也不是SpringBoot專案,專案結構如下: POM檔案: ```xml
4.0.0 com.dmz.framework mybatis 1.0-SNAPSHOT mysql mysql-connector-java 8.0.15 org.springframework spring-context 5.2.6.RELEASE org.springframework
spring-aop 5.2.6.RELEASE
org.aspectj aspectjweaver 1.9.5
``` 配置類: ```java // 開啟AOP跟掃描元件即可 @EnableAspectJAutoProxy @ComponentScan("com.dmz.mybatis.tx_demo") public class Config { } ``` 完成事務管理的核心類: ```java public class TransactionUtil { public static final ThreadLocal synchronousConnection = new ThreadLocal(); 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(); } } } ``` 實際需要事務管理的類 ```java @Component public class UserService { public void saveUser() { TransactionUtil.execute ("INSERT INTO `test`.`user`(`id`, `name`) VALUES (?, ?)", 100, "dmz"); // 測試回滾 // throw new RuntimeException(); } } ``` 切面: ```java @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 { // 在方法執行前開啟事務 TransactionUtil.startTransaction(); // 執行業務邏輯 Object proceed = null; try { ProceedingJoinPoint method = (ProceedingJoinPoint) joinPoint; proceed = method.proceed(); } catch (Throwable throwable) { // 出現異常進行回滾 TransactionUtil.rollback(); return proceed; } // 方法執行完成後提交事務 TransactionUtil.commit(); return proceed; } } ``` 用於測試的主函式: ```java public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); UserService userService = ac.getBean(UserService.class); userService.saveUser(); } } ``` 具體的測試過程跟測試結果我就不放了,大家把程式碼拷貝過去自行測試就好了 # 總結 本文主要介紹了Spring中的事務相關內容,對Spring中的事務抽象機制做了介紹,主要是為了讓大家在接下來一篇原始碼文章中能減輕負擔,希望大家可以根據自己理解動手模擬下Spring中事務的實現哦,當你自己去實現的時候肯定會碰到一系列的問題,然後帶著這些問題看原始碼你才能知道Spring為什麼要做這些事情! 如果本文對你由幫助的話,記得點個贊吧!也歡迎關注我的公眾號,微信搜尋:程式設計師DMZ,或者掃描下方二維碼,跟著我一起認認真真學Java,踏踏實實做一個coder。 ![公眾號](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci8lRTUlODUlQUMlRTQlQkMlOTclRTUlOEYlQjcuanBn?x-oss-process=image/format,png) 我叫DMZ,一個在學習路上匍匐前行的小