1. 程式人生 > >Spring3.1.0實現原理分析(二十二).Dao事務分析之事務管理器DataSourceTransactionManager

Spring3.1.0實現原理分析(二十二).Dao事務分析之事務管理器DataSourceTransactionManager

       大家好,開篇先來談談spring事務的優點吧,即spring事務的存在價值。首先它提供了非侵入式編碼的事務實現,這個是通過aop實現的,具體的實現過程之前也寫部落格分析了。

       另外,spring還提供了一套標準的事務管理工作流程。簡單的說,事務管理一共可分為三個步驟,分別是初始化事務、提交事務、回滾事務,然後每個步驟又可細分為若干小步驟。spring事務工作流相當於為使用者遮蔽了具體orm框架的底層處理邏輯,基於spring開發的程式,即便更換了orm框架,即便從本地事務切換到全域性事務,也只需要簡單的更改配置,選用合適的事務管理器,基本不會修改程式碼,這就是spring事務另一個優點。

     

       Spring的PlatformTransactionManager是事務管理的頂層介面,其中定義的三個方法對應的就是上述的三個步驟,然後AbstractPlatformTransactionManager抽象類給出了三個步驟的具體實現。當然,AbstractPlatformTransactionManager也留下了幾個未實現的抽象方法,具體有“建立事務物件”,“開始事務”,“執行提交”,“執行回滾”,這幾個抽象方法的實現跟具體的orm框架(如:mybatis,hibernate)或事務特性(如:本地事務,全域性事務)有關。比如今天要介紹的主角DataSourceTransactionManager

,它實現了上述的抽象方法,它適用的場景是“mybatis”,“ jdbctemplate”,"本地事務"。下面我們就具體分析DataSourceTransactionManager的事務管理三大步驟詳細處理流程。

一. DataSourceTransactionManager初始化事務

1. 獲取事務名稱 

    預設事務名稱是被代理類的全限定名稱+當前被攔截的方法名稱,如: testmybatis.TxDao.findList

2. 獲取事務屬性物件(TransactionDefinition)

    事務屬性物件持有事務的相關配置,比如事務的隔離級別,傳播行為,是否只讀等。我們開啟spring事務管理時,通常都會在配置檔案里加入這樣一段配置。

<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="get*" read-only="true"/>
		<tx:method name="*"/>
	</tx:attributes>
</tx:advice>

     spring解析上述配置會建立兩個事務屬性物件。第一個事務屬性物件適用於get字首的方法物件,它是隻讀事務;第二個事務物件匹配非get字首的其它方法,它使用預設事務配置,預設配置的話非只讀,事務超時時間為-1,隔離級別使用資料庫預設配置,傳播行為是PROPAGATION_REQUIRED。

3. 獲取事務管理器

    事務管理器跟事務屬性物件一樣,通常使用者都已經配置好了,直接從spring的bean工廠獲取,另外從配置也看出DataSourceTransactionManager持有dataSource

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="jdbcDataSource" />
</bean>
4. 建立事務物件

    這個方法是AbstractPlatformTransactionManager定義的抽象方法,DataSourceTransactionManager實現了這個抽象方法,它建立的事務物件型別是DataSourceTransactionObject,事務管理器和事務物件之間是存在對應關係的。然後嘗試從當前執行緒獲取ConnectionHolder(連線持有器),賦值給事務物件。ConnectionHolder實現了ResourceHolder介面,它持有一個數據庫連線,可以把凡是實現了ResourceHolder介面的物件看做是一種資源物件。預設情況下此時當前執行緒是不存在ConnectionHolder的,因此獲取不到插播一句,在spring事務管理過程中會用到一些執行緒安全物件,這些物件都交由TransactionSynchronizationManager管理,TransactionSynchronizationManager把這些物件都儲存在ThreadLocal中

5. 獲取被掛起資源持有器

    被掛起資源持有器持有兩類被掛起的資源,一類是資源物件,如上述的ConnectionHolder,一類是同步物件,即實現了TransactionSynchronization介面的物件。預設在這個時候事務同步還未被啟用,因此返回null。

6. 建立事務狀態物件

    被建立的事務狀態物件型別是DefaultTransactionStatus,它持有上述建立的事務物件。事務狀態物件主要用於獲取當前事務物件的狀態,比如事務是否被標記了回滾,是否是一個新事務等等。

7. 開始事務 

    7.1. 由於此時事務物件DataSourceTransactionObject持有的ConnectionHolder為null,因此首先需要建立ConnectionHolder物件,建立ConnectionHolder物件的前置條件是要先獲取資料庫連線物件,於是從dataSource獲取連線物件,把連線設定成手動提交,完成ConnectionHolder物件的建立,然後賦值給事務物件。另外這個時候上述的事務屬性物件派上用處了,根據事務屬性物件配置連線的相關屬性,如隔離級別、超時時間等。

    7.2. 把資源物件ConnectionHolder置入執行緒安全的map,key是dataSource物件,value是ConnectionHolder物件本身,當orm框架執行資料庫操作需要連線物件時,獲得就是這個ConnectionHolder持有的連線

8. 預準備事務同步(主要是呼叫TransactionSynchronizationManager物件的方法,設定事務相關同步物件)

    8.1. 設定當前執行緒事務啟用為true。

    8.2. 把事務隔離級別設定到當前執行緒。

    8.3. 把事務是否只讀設定到當前執行緒。

    8.4. 把事務名稱設定到當前執行緒。

    8.5. 例項化當前執行緒的同步物件,預設是一個空集。

9. 建立事務資訊物件(TransactionInfo)

    事務資訊物件持有“事務管理器”,“事務屬性物件”,“事務狀態物件”,可以把事務資訊物件看做對以上幾個物件的打包。然後把事務資訊物件置入當前執行緒。

    

二. ORM框架(mybatis)執行資料庫操作

1. 通過mybatis's DefaultSessionFactory獲取defaultSqlSession。

2. 根據defaultSqlSession建立SqlSessionHolder(資源物件),把SqlSessionHolder置入當前執行緒,key是DefaultSessionFactory。

3. 建立SqlSessionSynchronization(同步物件),它持有的資源物件是SqlSessionHolder,把同步物件註冊到當前執行緒

4. 從當前執行緒獲取ConnectionHolder物件,key是dataSource,從ConnectionHolder獲取連線物件,

5. 執行資料庫操作。

關於同步物件,資源物件和事務之間的關係請看下圖:


三. DataSourceTransactionManager回滾事務

1. 判斷是否有必要對丟擲的異常執行回滾操作,預設只要異常物件派生自RuntimeException或Error,都會執行回滾操作。這個使用者是可以配置的,比如這樣。

<tx:attributes>
	<tx:method name="*" rollback-for="com.cn.untils.exception.XyzException"/>
</tx:attributes>
2. 呼叫事務管理器的rollback(transactionStatus)方法,傳入事務狀態物件。

    2.1. 觸發同步物件的beforeCompletion方法。

    2.2. 從事務物件獲取連線持有器,然後再獲取連線物件,呼叫連線的rollback()方法。

    2.3. 觸發同步物件的afterCompletion方法。

四. DataSourceTransactionManager提交事務

1. 呼叫事務管理器的rollback(transactionStatus)方法,傳入事務狀態物件。

    1.1. 觸發同步物件的beforeCommit方法。

    1.2. 觸發同步物件的beforeCompletion方法。

    1.3. 從事務物件獲取連線持有器,然後再獲取連線物件,呼叫連線的commit()方法。

    1.4. 觸發同步物件的afterCommit方法。

    1.5. 觸發同步物件的afterCompletion方法。ro