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

前言

本專題大綱以下:java

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

事務專題大綱mysql

「對於專題大綱我又作了調整哈,主要是但願專題的內容可以更豐富,更加詳細」,原本是想在源碼分析的文章中附帶講一講事務使用中的問題,這兩天想了想仍是單獨寫一篇並做爲事務專題的收尾篇,也是我Spring源碼專題的收尾篇。程序員

本文大綱以下:spring

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

Spring事務應用大綱sql

在看這篇文章,以及下篇源碼分析的文章我但願你對Spring AOP以及有充分的瞭解,否則一些細節問題你可能看不明白,關於Spring AOP若是你能看完這三篇文章基本上就沒什麼問題了數據庫

  • Spring官網閱讀(十八)AOP的核心概念apache

  • Spring中AOP相關的API及源碼解析,原來AOP是這樣子的編程

  • 你知道Spring是怎麼將AOP應用到Bean的生命週期中的嗎?

編程式事務

Spring提供了兩種編程式事務管理的方法安全

  • 使用 TransactionTemplate 或者 TransactionalOperator.
  • 直接實現TransactionManager接口

若是是使用的是命令式編程,Spring推薦使用TransactionTemplate 來完成編程式事務管理,若是是響應式編程,那麼使用TransactionalOperator更加合適。session

TransactionTemplate

使用示例(我這裏直接用的官網提供的例子了)

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方法的實現:

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.經過事務管理器開啓事務
   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。

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. 正常執行則提交事務

這裏我仍是直接用官網給出的例子

// 定義事務
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配置
  • 基於註解

申明式事務事務的實現原理以下(圖片來源於官網):

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

實現原理

「實際上就是結合了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接口定義

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

接口定義以下:

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,該類的繼承關係以下:

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

RuleBasedTransactionAttribute

  • DefaultTransactionDefinition,實現了TransactionDefinition,併爲其中的定義的屬性提供了默認值
// 默認的傳播機制爲required,沒有事務新建一個事務
// 有事務的話加入當前事務
private int propagationBehavior = PROPAGATION_REQUIRED;

// 隔離級別跟數據庫默認的隔離級別一直
private int isolationLevel = ISOLATION_DEFAULT;

// 默認爲-1,不設置超時時間
private int timeout = TIMEOUT_DEFAULT;

// 默認不是隻讀的
private boolean readOnly = false;
  • TransactionAttribute,擴展了`DefaultTransactionDefinition,新增了兩個事務的屬性
// 用於指定事務使用的事務管理器的名稱
String getQualifier();
// 指定在出現哪一種異常時才進行回滾
boolean rollbackOn(Throwable ex);
  • DefaultTransactionAttribute,繼承了DefaultTransactionDefinition,同時實現了TransactionAttribute接口,定義了默認的回滾異常
// 拋出RuntimeException/Error才進行回滾
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}
  • RuleBasedTransactionAttribute,@Transactional註解的rollbackFor等屬性就會被封裝到這個類中,容許程序員本身定義回滾的異常,若是沒有指定回滾的異常,默認「拋出RuntimeException/Error才進行回滾」

TransactionStatus

這個接口主要用於描述Spring事務的狀態,其繼承關係以下:

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

TransactionStatus

  • TransactionExecution,這個接口也是用於描述事務的狀態,TransactionStatus是在其上作的擴展,內部定義瞭如下幾個方法
// 判斷當前事務是不是一個新的事務
// 不是一個新事務的話,那麼須要加入到已經存在的事務中
boolean isNewTransaction();

// 事務是否被標記成RollbackOnly
// 若是被標記成了RollbackOnly,意味着事務只能被回滾
void setRollbackOnly(); 
boolean isRollbackOnly();

// 是否事務完成,回滾或提交都意味着事務完成了
boolean isCompleted();
  • SavepointManager,定義了管理保存點(Savepoint)的方法,隔離級別爲NESTED時就是經過設置回滾點來實現的,內部定義了這麼幾個方法
// 建立保存點
Object createSavepoint() throws TransactionException;

// 回滾到指定保存點
void rollbackToSavepoint(Object savepoint) throws TransactionException;

// 移除回滾點
void releaseSavepoint(Object savepoint) throws TransactionException;
  • TransactionStatus,繼承了上面這些接口,額外提供了兩個方法
//用於判斷當前事務是否設置了保存點
boolean hasSavepoint();

// 這個方法複寫了父接口Flushable中的方法
// 主要用於刷新會話
// 對於Hibernate/jpa而言就是調用了其session/entityManager的flush方法
void flush();


小總結:
經過上面的分析咱們會發現,TransactionDefinition的主要做用是給出一份事務屬性的定義,而後事務管理器根據給出的定義來建立事務,TransactionStatus主要是用來描述建立後的事務的狀態

在對TransactionDefinition跟TransactionStatus有必定了解後,咱們再回到PlatformTransactionManager接口自己,PlatformTransactionManager做爲事務管理器的基礎接口只是定義管理一個事務必須的三個方法:開啓事務,提交事務,回滾事務,接口僅僅是定義了規範而已,真正作事的仍是要依賴它的實現類,因此咱們來看看它的繼承關係

PlatformTransactionManager的實現類

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

PlatformTransactionManager

  • AbstractPlatformTransactionManager,Spring提供的一個事務管理的基類,提供了事務管理的模板,實現了Spring事務管理的一個標準流程
  1. 判斷當前是否已經存在一個事務
  2. 應用合適的事務傳播行爲
  3. 在必要的時候掛起/恢復事務
  4. 提交時檢查事務是否被標記成爲rollback-only
  5. 在回滾時作適當的修改(是執行真實的回滾/仍是將事務標記成rollback-only)
  6. 觸發註冊的同步回調
  • 在AbstractPlatformTransactionManager提供了四個常見的子類,其說明以下

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

image-20200806194650050
關於事務管理器的詳細代碼分析放到下篇文章,本文對其有個大概瞭解便可。

Spring中事務的同步機制

Spring中事務相關的同步機制能夠分爲兩類

  • 資源的同步
  • 行爲的同步

什麼是資源的同步呢?在一個事務中咱們每每會一次執行多個SQL(若是是單條的SQL實際上沒有必要開啓事務),爲了保證事務全部的SQL都可以使用一個數據庫鏈接,這個時候咱們須要將數據庫鏈接跟事務進行同步,這個時候數據庫鏈接就是跟這個事務同步的一個資源。

那什麼又是行爲的同步呢?仍是以數據庫鏈接爲例子,在事務開啓以前咱們須要先獲取一個數據庫鏈接,一樣的在事務提交時咱們須要將鏈接關閉(不必定是真正的關閉,若是是鏈接池只是歸還到鏈接池中),這個時候關閉鏈接這個行爲也須要跟事務進行同步

那麼Spring是如何來管理同步的呢?一樣的,Spring也提供了一個同步管理器TransactionSynchronizationManager,這是一個抽象類,其中全部的方法都是靜態的,而且全部的方法都是圍繞它所申明的幾個靜態常量字段,以下:

// 這就是同步的資源,Spring就是使用這個完成了鏈接的同步
private static final ThreadLocal<Map<Object, Object>> resources =
    new NamedThreadLocal<>("Transactional resources");

// TransactionSynchronization完成了行爲的同步
// 關於TransactionSynchronization在後文進行分析
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
    new NamedThreadLocal<>("Transaction synchronizations");

// 事務的名稱
private static final ThreadLocal<String> currentTransactionName =
    new NamedThreadLocal<>("Current transaction name");

// 事務是否被標記成只讀
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");

// 是否真實開啓了事務 
private static final ThreadLocal<Boolean> actualTransactionActive =
    new NamedThreadLocal<>("Actual transaction active");

能夠看到全部的同步都是經過ThreadLocal實現的,對於ThreadLocal本文不作詳細分析,若是對ThreadLocal還不瞭解的同窗也沒有關係,對於本文而言你只須要知道ThreadLocal能將資源跟當前線程綁定便可,例如ThreadLocal<Map<Object, Object>> resources這個屬性就表明要將一個map綁定到當前線程,它提供了set跟get方法,分別用於將屬性綁定到線程上以及獲取線程上綁定的屬性。

上面的幾個變量中除了synchronizations以外其他的應該都很好理解,synchronizations中綁定的是一個TransactionSynchronization的集合,那麼這個TransactionSynchronization有什麼用呢?咱們來看看它的接口定義

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 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 {

}

完成事務管理的核心類:

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();
        }
    }
}

實際須要事務管理的類

@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 {
        // 在方法執行前開啓事務
        TransactionUtil.startTransaction();

        // 執行業務邏輯
        Object proceed = null;
        try {
            ProceedingJoinPoint method = (ProceedingJoinPoint) joinPoint;
            proceed = method.proceed();
        } catch (Throwable throwable) {
            // 出現異常進行回滾
            TransactionUtil.rollback();
            return proceed;
        }
        // 方法執行完成後提交事務
        TransactionUtil.commit();
        return proceed;
    }
}

用於測試的主函數:

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,一個但願慢慢進步的小菜鳥~!

往期精選

  • Spring官網閱讀筆記
  • Spring雜談
  • JVM系列文章
  • Spring源碼專題

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

程序員DMZ點贊、轉發、在看,多謝多謝!喜歡做者

相關文章
相關標籤/搜索