欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析Spring的事務實現(xiàn)原理

 更新時間:2022年11月10日 08:56:22   作者:DrLauPen  
這篇文章主要為大家詳細介紹了Spring中事務實現(xiàn)的原理,文中的示例代碼講解詳細,對我們學習Spring有一定的幫助,需要的可以參考一下

SQL事務實現(xiàn)簡介

首先我們來了解下,最簡單的事務是怎么實現(xiàn)的呢?以JDBC為例,當一個數(shù)據(jù)庫Connection對象創(chuàng)建后,其會默認自動提交事務;每次執(zhí)行SQL語句時,如果成功,就會向數(shù)據(jù)庫自動提交,不能回滾。

通過調(diào)用setAutoCommit(false)方法可以取消自動提交事務。等到所有的SQL語句都執(zhí)行成功后,調(diào)用commit()方法提交事務。如果其中某個操作失敗或出現(xiàn)異常時,則調(diào)用rollback()方法回滾事務。具體代碼如下所示:

    public void noTransaction() {
        Connection connection = null;
        String sql = "update account set balance=balance-100 where id=1";
        String sql2 = "update account set balance=balance+100 where id=2";
        //創(chuàng)建PreparedStatement對象
        PreparedStatement preparedStatement = null;
        try {
            connection = JDBCUtils.getConnection();// 獲取數(shù)據(jù)庫連接
            connection.setAutoCommit(false);//事務開始
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate();//執(zhí)行第一個sql
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();//執(zhí)行sql2
            //提交事務
            connection.commit();
        } catch (SQLException e) {
            //進行事務回滾,默認回滾到事務開始的地方
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //關閉流
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }

將代碼抽象成執(zhí)行步驟,主要有以下四步:

  • 獲取Mysql鏈接
  • 執(zhí)行SQL語句
  • 提交SQL事務
  • 存在異常則做Mysql的事務回滾。

可以發(fā)現(xiàn),常規(guī)情況下只有執(zhí)行SQL語句的內(nèi)容存在差異。如果能將相同部分抽取出來,接入方接入時只考慮SQL語句內(nèi)容,就可以減少接入的成本。同時觀察到抽取的部分處于執(zhí)行SQL語句的前后,那么很自然的就可以想到兩種解決方案:

1、在JAVA8中,提供了函數(shù)式編程。我們可以將要執(zhí)行的SQL語句封裝成函數(shù),作為入?yún)魅氩?zhí)行。

2、采用動態(tài)代理對執(zhí)行SQL的前后做增強。

編程式事務

Spring中采用函數(shù)式編程實現(xiàn)的事務,被稱為編程式事務。編程式事務的實現(xiàn)相對簡單,主要由類TransactionTemplate負責實現(xiàn)。具體代碼可以見如下所示:

@Override
@Nullable
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 {
       //獲取事務 
      TransactionStatus status = this.transactionManager.getTransaction(this);
      T result;
      try {
          //執(zhí)行SQL語句內(nèi)容
         result = action.doInTransaction(status);
      }
      catch (RuntimeException | Error ex) {
         //異?;貪L
         rollbackOnException(status, ex);
         throw ex;
      }
      catch (Throwable ex) {
         // 異?;貪L
         rollbackOnException(status, ex);
         throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
      }
       //提交事務
      this.transactionManager.commit(status);
      return result;
   }
}

TransactionCallBack作為入?yún)魅耄渲芯椭饕俏覀円獔?zhí)行的SQL語句內(nèi)容。而其余部分可以看到,其實就和我們前面所描述的四步基本相似:

  • 獲取Mysql鏈接
  • 執(zhí)行SQL語句
  • 提交SQL事務
  • 存在異常則做Mysql的事務回滾。

聲明式事務

在Spring中,采用AOP做增強邏輯的被稱為聲明式事務。相比起編程式事務,聲明式事務相對復雜。因此,在了解聲明式事務之前,我們需要先簡單了解一下Spring是如何支持AOP(動態(tài)代理)。首先我們知道,Spring中Bean的存在形式有以下幾個階段:

其中非常關鍵點就在BeanFactory。當我們對一個Bean定義代理對象后,BeanFactory生成的就不會是單純的Bean實例對象,而是Bean的動態(tài)代理。通過調(diào)用Bean的動態(tài)代理中的方法,來實現(xiàn)AOP。那么如何自定義自己的AOP呢?要實現(xiàn)AOP需要明確兩個點:

1、需要在哪里做增強?(定義切點)

2、需要做什么樣的增強邏輯?(定義增強邏輯)

對于這兩點,Spring主要通過**事務代理管理配置類(ProxyTransactionManagementConfiguration)**進行實現(xiàn)。

從類圖中可以看到,事務代理管理配置類主要定義了三個Bean對象:

  • 注釋事務屬性源(AnnotationTransactionAttributeSource),其主要負責判斷當前類是否為需要增強的類,即"哪里需要做增強"。
  • 事務攔截器(TransactionInterceptor),該類主要負責對事務做鏈接獲取、事務提交以及事務回滾。即"怎么做增強"。
  • Bean工廠事務屬性源指導(BeanFactoryTransactionAttributeSourceAdvisor),這個與事務本身無關,主要是在Bean工廠生產(chǎn)Bean實例的時候,方便對Bean進行替換使用的。其中主要是負責將定義的切點和增強邏輯注入到Spring中。

這里我們逐一來介紹這三個Bean對象。

注釋事務屬性源

"哪里需要做增強",意味著類要具備判斷是否需增強的能力。為此,注釋事務屬性源提供了一個關鍵的方法:isCandidateClass()。

? 但聲明事務的注解一定不只一種。如果需要識別所有包下的事務型注解,一定會需要多次判斷。因此,在注解事務屬性源中,還保存了一組接口對象事務注釋解析器(TransactionAnnotationParser),通過循環(huán)遍歷這組事務注釋解析器,就可以對不同框架注解進行處理。具體源碼如下:

@Override
public boolean isCandidateClass(Class<?> targetClass) {
   for (TransactionAnnotationParser parser : this.annotationParsers) {
      if (parser.isCandidateClass(targetClass)) {
         return true;
      }
   }
   return false;
}

以SpringTransactionAnnotationParser注釋解析器為例,其實現(xiàn)的isCandidateClass()方法判斷類是否被@Transactional類注釋了,如果是,那么該類就是潛在的候選類。

@Override
public boolean isCandidateClass(Class<?> targetClass) {
   return AnnotationUtils.isCandidateClass(targetClass, Transactional.class);
}

依次類推,對@TransactionAttribute等其他框架的注釋,我們都可以采用這樣方法實現(xiàn)。

事務攔截器

具備了判斷哪些類需要執(zhí)行事務的能力后,我們還需要確定具體的增強邏輯是什么樣子的。而這就是事務攔截器主要功能。要實現(xiàn)這個功能,需要在對應方法被調(diào)用時,執(zhí)行增強方法。

從類圖首先可以看到,為了能夠察覺到方法的調(diào)用,事務攔截器實現(xiàn)了方法攔截器接口(MethodInterceptor)的invoke方法,在invoke方法中先判斷當前執(zhí)行的方法屬于哪個類,緊接著會用invokeWithinTransaction()對方法進行事務性的包裝。其源碼如下:

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
    // 判斷執(zhí)行的方法屬于哪個類
   Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    //再調(diào)用事務進行執(zhí)行
   return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
      @Override
      @Nullable
      public Object proceedWithInvocation() throws Throwable {
         return invocation.proceed();
      }
      @Override
      public Object getTarget() {
         return invocation.getThis();
      }
      @Override
      public Object[] getArguments() {
         return invocation.getArguments();
      }
   });
}

主要邏輯放在invokeWithinTransaction()方法中。在該方法中,主要考慮了三類不同的編程方式的事務,分別是:響應式事務(ReactiveTransactionManager)、回調(diào)優(yōu)先型事務(CallbackPreferringPlatformTransactionManager)非回調(diào)優(yōu)先型事務(非CallbackPreferringPlatformTransactionManager)

三者的差異主要在于:

1、響應式編程常采用Mono或Flux實現(xiàn),需要對兩種方式選擇相應適配器做適配。

2、后兩者從名字上可以看出差異,回調(diào)型優(yōu)先的事務,會先執(zhí)行回調(diào)再執(zhí)行事務。而非回調(diào)優(yōu)先型事務,則關注于事務的執(zhí)行,至于回調(diào)的失敗與否不需要影響事務的回滾。

盡管三者存在一些差異,但他們對于事務的實現(xiàn)其實是相似的,這里以非回調(diào)優(yōu)先型事務為例子:

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
      final InvocationCallback invocation) throws Throwable {
   TransactionAttributeSource tas = getTransactionAttributeSource();
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
   final TransactionManager tm = determineTransactionManager(txAttr);
	.......
        
   PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
   final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
   if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
      // 創(chuàng)建事務
      TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
      Object retVal;
      try {
         // 執(zhí)行方法
         retVal = invocation.proceedWithInvocation();
      } catch (Throwable ex) {
         // 回滾處理 + 拋出異常終止執(zhí)行
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      } finally {
         cleanupTransactionInfo(txInfo);
      }
		// 正常執(zhí)行了事務,此時再執(zhí)行回調(diào)
      if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
         TransactionStatus status = txInfo.getTransactionStatus();
         if (status != null && txAttr != null) {
            retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
         }
      }
		// 提交事務
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }
}

源碼本身不復雜,可以看到也是四步:

  • 獲取Mysql鏈接信息
  • 執(zhí)行SQL語句
  • 提交SQL事務
  • 存在異常則做Mysql的事務回滾。

Bean工廠事務屬性源指導

對于Bean工廠事務屬性源指導,其主要負責用于定義切點和增強邏輯,并將這些事務的邏輯注冊到Spring中用于實現(xiàn)。如下是Bean工廠事務屬性源指導的類圖。

從類圖上可以看到,其繼承了AbstractPointcutAdvisor關鍵模版類,該類是Spring中用于定義切點和增強邏輯。通過指定PointCut和Advice,就可以實現(xiàn)自定義的增強邏輯。因此,Bean工廠事務屬性源指導只要將事務攔截器標記為增強邏輯,將注釋事務屬性源標記為切點,就可以讓其在Spring中作為AOP生效。

通過這三者的合作:注釋事務屬性源標注了切點(說明我那些方法需要做增強);事務攔截器定義了要執(zhí)行的增強邏輯(說明我對這些方法怎么做增強);Bean工廠事務屬性源指導則將切點和增強邏輯注入到Spring中使其生效。從而實現(xiàn)了Spring的聲明式事務的內(nèi)容。

事務多樣性支持

在前述內(nèi)容中,我們思考了SQL情況下如何實現(xiàn)事務。但有個問題,如果數(shù)據(jù)源換成Redission、換成分布式事務的API,代碼還能快速復用么?簡而言之,Spring是如何支持數(shù)據(jù)源多樣性?如何確保新數(shù)據(jù)源的快速接入?

對實現(xiàn)事務的流程做進一步抽象,不難發(fā)現(xiàn)一次事務中,框架需要關注的功能其實只有三個:

  • 獲取事務鏈接
  • 提交事務
  • 事務回滾

因此,對不同的數(shù)據(jù)源,都可以將其抽象成這三個能力。應用層只需要對這三個能力進行調(diào)用,就不會在因為下層數(shù)據(jù)源的差異而需要大幅度的改動。而這正與面向接口設計的思想不謀而合

為此,Spring專門設計了接口PlatformTransactionManager,其主要負責對外提供三個方法:getTransaction(definition)、commit(status)、rollback(status)。就用來抽象的上述的三個功能。由此一來,應用層的代碼實現(xiàn)類(這里以TransactionTemplate為例子)就不再需要依賴于我的數(shù)據(jù)源究竟是JDBC、Redission還是DataSource。面對抽象編程,從而減少了接入需要考慮不同類型所帶來的成本。

總結(jié)

本文介紹了Spring中針對SQL事務實現(xiàn)的兩種方式:編程式事務聲明式事務。同時介紹了對于多種不同的數(shù)據(jù)源,Spring在設計上的架構(gòu)實現(xiàn),希望對大家后續(xù)的開發(fā)設計有所幫助。

以上就是淺析Spring的事務實現(xiàn)原理的詳細內(nèi)容,更多關于Spring事務的資料請關注腳本之家其它相關文章!

相關文章

  • springMVC攔截器HandlerInterceptor用法代碼示例

    springMVC攔截器HandlerInterceptor用法代碼示例

    這篇文章主要介紹了springMVC攔截器HandlerInterceptor用法代碼示例,具有一定借鑒價值,需要的朋友可以參考下
    2017-12-12
  • Java編寫迷宮小游戲

    Java編寫迷宮小游戲

    最近經(jīng)常在機房看同學在玩一個走迷宮的游戲,比較有趣,自己也用java寫一個實現(xiàn)隨機生成迷宮的算法,其實就是一個圖的深度優(yōu)先遍歷算法.
    2016-05-05
  • Java中常用時間的一些相關方法

    Java中常用時間的一些相關方法

    日期的使用多種多樣,但萬變不離其宗,下面這篇文章主要給大家介紹了關于Java中常用時間的一些相關方法,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-10-10
  • 詳述IntelliJ IDEA 中自動生成 serialVersionUID 的方法(圖文)

    詳述IntelliJ IDEA 中自動生成 serialVersionUID 的方法(圖文)

    本篇文章主要介紹了詳述IntelliJ IDEA 中自動生成 serialVersionUID 的方法(圖文),具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-11-11
  • Java正則之貪婪匹配、惰性匹配

    Java正則之貪婪匹配、惰性匹配

    這篇文章主要介紹了Java正則之貪婪匹配、惰性匹配的相關資料,需要的朋友可以參考下
    2015-03-03
  • Java接口和抽象類用法實例總結(jié)

    Java接口和抽象類用法實例總結(jié)

    這篇文章主要介紹了Java接口和抽象類用法,結(jié)合實例形式總結(jié)分析了Java接口與抽象類的具體定義、使用技巧與相關注意事項,需要的朋友可以參考下
    2015-12-12
  • 100-200之間所有素數(shù)求和程序代碼(二個版本)

    100-200之間所有素數(shù)求和程序代碼(二個版本)

    寫一個求100-200之間素數(shù),并求和的程序,大家參考使用吧
    2013-11-11
  • Spring中@RefreshScope注解的處理方法詳解

    Spring中@RefreshScope注解的處理方法詳解

    這篇文章主要介紹了Spring中@RefreshScope注解的處理方法詳解,spring啟動時會調(diào)用ClassPathBeanDefinitionScanner.java類中的doScan()對包路徑下的所有class進行掃描,獲取bean的定義,同時對bean的@RefreshScope(@Scope的父類)進行處理,需要的朋友可以參考下
    2023-10-10
  • Java對時間的簡單操作實例

    Java對時間的簡單操作實例

    這篇文章主要介紹了Java對時間的簡單操作,實例分析了針對java.util.Date的各類常見操作,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-01-01
  • 解決restlet client報錯No response.Is the certificate valid? Click here to check.

    解決restlet client報錯No response.Is the cer

    這篇文章主要介紹了解決restlet client報錯No response.Is the certificate valid? Click here to check.問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01

最新評論