天天看點

Spring Transaction 源碼解析寫在前面1.什麼是事務?2.Spring 事務介紹3.Spring Transaction 源碼分析從何入手4.Spring Transaction 源碼分析時序圖  單擊放大檢視(高清圖下載下傳請轉至文末連結)5.源碼分析

寫在前面

        在分析 Spring AOP 源碼之前,如果你對 Spring IOC、依賴注入(DI) 原理不是很清楚,建議您先了解一下:Spring IOC 源碼解析、Spring AOP 源碼解析、Spring 依賴注入(DI) 源碼解析,這樣或許會讓你的思路更加清晰。在源碼解析之前,我們先來介紹一下

事務

這個概念。

1.什麼是事務?

        事務(Transaction),是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。 事務處理可以確定除非事務性單元内的所有操作都成功完成,否則不會永久更新面向資料的資源。

簡單了解事務,即:目前操作要麼全部成功,要麼全部失敗。

這樣可以簡化錯誤恢複并使應用程式更加可靠。

Ⅰ.事務的四個特性(ACID)

  事務 4 大特性:

原子性

一緻性

隔離性

持久性

。通常稱為 ACID 特性。

  1. 原子性(Atomicity):指一個事務是一個不可分割的工作單元,事務中包括的所有操作要麼都完成,要麼都不完成。
  2. 一緻性(Consistency):指事務必須是使資料庫從

    一個一緻性狀态

    變到

    另一個一緻性狀态

    ,是否一緻性與原子性是密切相關的。(通常情況下,原子性 和 一緻性 都是在一起介紹的)
  3. 隔離性(Isolation):指一個事務的執行不能被其他事務幹擾。即一個事務内部的操作及使用的資料對 并發的其他事務是隔離的,并發執行的各個事務之間不能互相幹擾。
  4. 持久性(Durability):指一個事務一旦送出,它對資料庫中資料的改變就應該是永久性的,接下來的其他操作或故障不應該對這些資料有任何影響。

  

一緻性

這個概念不好了解,舉個栗子。eg: 比如銀行有2000塊,A1000,B1000。A轉賬100給B,中間出現異常,也不會影響銀行2000塊這個總數的不一緻,不會出現A:1100 、 B 1000 這種情況。

Ⅱ.資料庫如何進行事務操作(以MySQL為例)

事務操作,隻針對資料庫的 C(插入)U(更新)D(删除) 操作。

因為

R(查詢)

并不會涉及到資料的變動,是以查詢操作不涉及到事務。此處以 插入 和 删除 操作來介紹。

Spring Transaction 源碼解析寫在前面1.什麼是事務?2.Spring 事務介紹3.Spring Transaction 源碼分析從何入手4.Spring Transaction 源碼分析時序圖  單擊放大檢視(高清圖下載下傳請轉至文末連結)5.源碼分析
Spring Transaction 源碼解析寫在前面1.什麼是事務?2.Spring 事務介紹3.Spring Transaction 源碼分析從何入手4.Spring Transaction 源碼分析時序圖  單擊放大檢視(高清圖下載下傳請轉至文末連結)5.源碼分析

此塊内容知識,如需更詳細了解,請點選以下連結學習:

  1. InnoDB 事務與鎖的前世今生
  2. 一文帶你了解 InnoDB 中的 MVCC、Undo、Redo 機制

Ⅲ.事務操作流程

  1. 開啟事務(open)
  2. 執行事務操作(execute)
  3. 成功:送出事務(自動送出:autocommit、手動送出)

    失敗:復原事務(rollback)

  4. 關閉事務(close)

       備注: 事務送出預設為

自動送出

。我們可以通過

con.setAutoCommit(true/false);

來設定。false即為手動送出。

2.Spring 事務介紹

        Spring 事務的本質其實就是資料庫對事務的支援,沒有資料庫對事務的支援,Spring 是無法提供事務功能的。對于純 JDBC 操作資料庫,想要用到事務,可以按照以下步驟進行:

public static void main(String[] args){
	//1.擷取連接配接
	Connection conn = DriverManager.getConnection();
	//2.開啟事務(true為自動送出事務,false為手動送出事務)
	conn.setAutoCommit(true/false); 
	//3.執行CRUD操作
	CRUD operator
	//4.送出事務/復原事務
	conn.commit() / conn.rollback();
	//5.關閉連接配接
	conn.close(); //關閉連接配接 
}
           

       使用 Spring 事務管理後,我們可以不再寫步驟 2 和 4 的代碼,而是由 Spirng 來幫我們自動完成。那麼 Spring 是如何在我們書寫的 CRUD 操作之前和之後開啟事務和關閉事務的呢?(此處用到了 Spring AOP 機制:Spring AOP 源碼解析)。下面就以 【注解方式】 為例來簡單介紹 Spring 是如何幫我們來管理事務的。

  1. 配置檔案開啟注解驅動,在相關的類和方法上通過注解@Transactional 辨別。
  2. Spring 在啟動時會去解析@Transactional 辨別,并生成相關的 bean,這時候會檢視擁有相關注解的類和方法,并且為這些類和方法生成代理,并根據@Transaction 的相關參數進行配置注入,這樣就在代理中為我們把相關的事務處理掉了(開啟正常送出事務,異常復原事務)

Ⅰ.Spring事務的配置方式

        Spring支援

1.程式設計式事務管理

以及

2.聲明式事務管理

兩種方式。程式設計式事務管理是侵入性事務管理,聲明式事務管理建立在AOP之上。下面我們就用這兩種方式,分别來簡單配置。

1.程式設計式事務管理

<beans>
	<!--1.配置資料源(拿到Connection連接配接對象)-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}" />
        <property name="url" value="${jdbc.jdbcUrl}" />
        <property name="username" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- 2.建立事務管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 3.Spring事務是利用AOP實作的,利用切面程式設計來實作對某一類方法進行事務統一管理(聲明式事務) -->
    <!-- expression表達式如何寫,請參考:https://blog.csdn.net/lzb348110175/article/details/95517753 -->
    <aop:config>
        <aop:pointcut id="transactionPointCut" expression="execution(public * com.mvc.service.*.*(..))"></aop:pointcut>
        <aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointCut"></aop:advisor>
    </aop:config>

    <!-- 4.配置事務通知規則 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
            <tx:method name="remove*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
            <tx:method name="modify*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
            <tx:method name="login" propagation="NOT_SUPPORTED"/>
            <tx:method name="query" read-only="true"/>
        </tx:attributes>
        
    </tx:advice>
</beans>
           

2.聲明式事務管理

<beans>
	<!-- 1.配置資料源(拿到Connection連接配接對象) -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${jdbc.driverClass}" />
		<property name="url" value="${jdbc.jdbcUrl}" />
		<property name="username" value="${jdbc.user}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	<!-- 2.事務管理器,依賴于資料源 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	<!-- 3.注冊事務管理驅動 -->
	<tx:annotation-driven transaction-manager="txManager"/>
<beans>
           
//4.在需要事務的類/方法上,添加 @Transactional 注解
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
public class AccountServiceImpl {
	//xxx
}

@Transactional(rollbackFor = { RuntimeException.class })
public void insert(RequestPara request) throws RuntimeException{}
           

Ⅱ.Spring事務的傳播屬性

        所謂 Spring 事務的傳播屬性,就是針對多個事務同時存在的時候,Spring 應該如何處理這些事務的行為。傳播屬性常量的解釋,如下表所示:

常量名 常量解釋
PROPAGATION_REQUIRED 支援目前事務,如果目前沒有事務,就建立一個事務。這是最常見的選擇,也是 Spring 預設的事務的傳播。
PROPAGATION_REQUIRES_NEW 建立事務,如果目前存在事務,把目前事務挂起。 建立的事務将和被挂起的事務沒有任何關系,是兩個獨立的事務,外層事務失敗復原之後,不能復原内層事務執行的結果,内層事務失敗抛出異常,外層事務捕獲,也可以不處理復原操作
PROPAGATION_SUPPORTS 支援目前事務,如果目前沒有事務,就以非事務方式執行。
PROPAGATION_NOT_SUPPORTED 以非事務方式執行操作,如果目前存在事務,就把目前事務挂起。
PROPAGATION_MANDATORY 支援目前事務,如果目前沒有事務,就抛出異常。
PROPAGATION_NEVER 以非事務方式執行,如果目前存在事務,則抛出異常。
PROPAGATION_NESTED 如果一個活動的事務存在,則運作在一個嵌套的事務中。如果沒有活動事務,則按 REQUIRED 屬性執行。它使用了一個單獨的事務,這個事務擁有多個可以復原的儲存點。内部事務的復原不會對外部事務造成影響。它隻對 DataSourceTransactionManager 事務管理器起效。

Ⅲ.資料庫隔離級别

隔離級别 髒讀 不可重複讀 幻讀 總結(導緻的問題)
READ UNCOMMITTED(未送出讀) 會導緻髒讀
READ COMMITTED(已送出讀) 可以解決髒讀問題,但是允許不可重複讀、幻讀
REPEATABLE READ(可重複讀)

可以解決髒讀,不可重複讀問題,但是允許幻讀

(注:InnoDB引擎可解決幻讀問題,其他引擎解決不了)

SERIALIZABLE(串行化)

串行化讀,事務隻能一個一個執行,可以解決髒讀、 不

可重複讀、幻讀。執行效率慢,使用時慎重

  • 髒讀 :一個事務對資料進行了增删改,但未送出,另一事務可以讀取到未送出的資料。如果第一個事務這時候復原了,那麼第二個事務就讀到了髒資料。
  • 不可重複讀:一個事務中發生了兩次讀操作,第一次讀操作和第二次操作之間,另外一個事務對資料進行了修改,這就會導緻兩次讀取的資料是不一緻的。
  • 幻讀:第一個事務對一定範圍的資料進行批量修改,第二個事務在這個範圍增加一條資料,這時候第一個事務就會丢失對新增資料的修改。

總結:

  1. 隔離級别越高,越能保證資料的完整性和一緻性,但是對并發性能的影響也越大。
  2. 大多數的資料庫預設隔離級别為 Read Commited(1),比如 SqlServer、Oracle
  3. 少數資料庫預設隔離級别為:Repeatable Read(2),比如: MySQL InnoDB

Ⅳ.Spring 事務中的隔離級别

隔離級别常量 常量解釋
ISOLATION_DEFAULT 這是個 PlatfromTransactionManager 預設的隔離級别,使用資料庫預設的事務隔離級别。

以下的四個與資料庫的隔離級别相對應。

ISOLATION_READ_UNCOMMITTED 這是事務最低的隔離級别,它允許另外一個事務可以看到這個事務未送出的資料。這種隔離級别會産生髒讀,不可重複讀和幻讀。
ISOLATION_READ_COMMITTED 保證一個事務修改的資料送出後才能被另外一個事務讀取。另外一個事務不能讀取該事務未送出的資料。
ISOLATION_REPEATABLE_READ 這種事務隔離級别可以防止髒讀,不可重複讀。但是可能出現幻讀。
ISOLATION_SERIALIZABLE 這是花費最高代價,但是最可靠的事務隔離級别。事務被處理為順序執行。

3.Spring Transaction 源碼分析從何入手

        Spring支援

1.程式設計式事務管理

以及

2.聲明式事務管理

兩種方式。這兩種方式我們都需要在 xml 檔案中進行配置。如:

<tx:advice>

<tx:annotation-driven>

。你先跳轉連結:1.Spring 如何解析自定義命名空間、2.Spring 自定義命名空間,了解一下 Spring 自定義命名空間的解析過程。然後我們再來看

<tx:advice>

這配置,便能夠了解 Spring Transaction 源碼應該從

TxNamespaceHandler

這個類開始分析。

4.Spring Transaction 源碼分析時序圖  單擊放大檢視(高清圖下載下傳請轉至文末連結)

   你也可以直接通路連結擷取:https://www.processon.com/view/link/5e6cb07ce4b0f2f3bd1f9ed1

Spring Transaction 源碼解析寫在前面1.什麼是事務?2.Spring 事務介紹3.Spring Transaction 源碼分析從何入手4.Spring Transaction 源碼分析時序圖  單擊放大檢視(高清圖下載下傳請轉至文末連結)5.源碼分析

   Spring 為我們提供了 3 個用于事務操作的接口:

  1. TransactionDefinition

    (事務定義)
  2. PlatformTransactionManager

    (事務管理器)
  3. TransactionStatus

    (事務的運作狀态)

        這 3 個接口在時序圖中,都有标注使用到的地方。時序圖分析,建議大家從

2.具體業務邏輯

處開始着手介入源碼分析,這幾部分都是關聯的。如果你對

Spring IOC 容器啟動

部分源碼都不是很了解,建議你先了解一下這部分再來看本文。飛機票給你們:【Spring事務畢竟是基于 AOP 來實作的,你也可以基于 AOP 時序圖來輔助學習,AOP 時序圖也在如下連結。分析源碼之路,注定不會一路平坦,加油】

  1. Spring IOC 源碼解析
  2. Spring AOP 源碼解析
  3. Spring 依賴注入(DI) 源碼解析

5.源碼分析

        此處不再一步步介紹源碼,你可以按照

4.Spring Transaction 源碼分析時序圖

,打開源碼來進一步分析,此處粘貼過多代碼無多大意義。

附 spring-framework-5.0.2.RELEASE (中文注釋)版本,直接解壓 IDEA 打開即可

位址: 1.spring-framework-5.0.2.RELEASE (中文注釋)版本

           2.網盤位址:spring-framework-5.0.2.RELEASE (中文注釋)版本(提取碼:uck4 )

恭喜您,枯燥源碼看到這裡。 Spring Transaction 源碼介紹到此為止

部落客寫作不易,來個關注呗

求關注、求點贊,加個關注不迷路 ヾ(◍°∇°◍)ノ゙

部落客不能保證寫的所有知識點都正确,但是能保證純手敲,錯誤也請指出,望輕噴 Thanks♪(・ω・)ノ