框架源碼系列十一:事務管理(Spring事務管理的特點、事務概念學習、Spring事務使用學習、Spring事務管理API學習、Spring事務源碼學習)


一、Spring事務管理的特點

Spring框架為事務管理提供一套統一的抽象,帶來的好處有:
1. 跨不同事務API的統一的編程模型,無論你使用的是jdbc、jta、jpa、hibernate。
2. 支持聲明式事務
3. 簡單的事務管理API
4. 能與Spring的數據訪問抽象層完美集成

說明:Spring的事物管理是用AOP實現的

二、事務概念學習

1. Isolation隔離級別

此事務與其他事務的工作隔離的程度。例如,該事務能否看到來自其他事務的未提交的寫操作
READ_UNCOMMITTED讀未提交
READ_COMMITTED讀提交
REPEATABLE_READ可重復讀
SERIALIZABLE序列化(串行)

2. Read/Write讀寫

該事務操作是讀、還是寫、還是有讀有寫

3. Timeout超時

對事務執行的時長設置一個閥值,如果超過閥值還未完成則回滾。

4. Propagation傳播行為

當一個方法開啟事務后,在方法中調用了其他的方法,其他方法可能也需要事務管理,此時就涉及事務該如何傳播了。
4.1. TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
4.2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
4.3. TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
4.4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
4.5. TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
4.6. TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
4.7. TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。

幾種事物傳播行為的情況:

5. SavePoint保存點

事務中可以設置一些保存點(階段標識),回滾時可以指定回滾到前面的哪個保存點。

6. Commit/Rollback提交/回滾

提交、回滾事務

三、Spring事務使用學習

1. 配置事務管理器

    <!-- 配置事務管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

2. 注解方式

2.1 開啟注解支持

在xml中開啟注解方式支持

<!-- 開啟注解方式的事務配置支持-->
 <tx:annotation-driven transaction-manager="txManager"/>

或在java代碼中以注解的方式(@EnableTransactionManagement)開啟注解方式支持

package com.study.leesmall.spring.sample.tx;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.study.leesmall.spring.sample.tx.entity.User;
import com.study.leesmall.spring.sample.tx.service.UserService;

@Configuration
@ComponentScan("com.study.leesmall.spring.sample.tx")
@ImportResource("classpath:com/study/leesmall/spring/sample/tx/application.xml")
@EnableTransactionManagement public class TxMain {

    public static void main(String[] args) {
        try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxMain.class);) {
            User user = new User();
            user.setId("1234564");
            user.setUserName("leesmall-666666666");

            UserService userService = context.getBean(UserService.class);
            userService.insertUser(user);
        }
    }
}

2.2 在要加事務管理的類或方法上加@Transactional注解

package com.study.leesmall.spring.sample.jta.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.study.leesmall.spring.sample.jta.dao.LogDao;
import com.study.leesmall.spring.sample.jta.entity.Log;

@Service
public class LogService {

    @Autowired
    private LogDao logDao;

    @Transactional public void insertLog(Log log) {
        this.logDao.insert(log);
    }

}

2.3 掌握@Transactional的屬性配置

說明:rollbackFor默認情況下是對RuntimeException進行回滾。

3. 聲明式事務配置

Spring提供基於AOP的聲明式事務管理,讓我們的事務管理變得簡單、易用!

    <!-- 數據源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" 
        init-method="init" destroy-method="close"> 
        <property name="driverClassName" value="${jdbc.driverClassName}" /> 
        <property name="url" value="${jdbc.url}" /> 
        <property name="username" value="${jdbc.username}" /> 
        <property name="password" value="${jdbc.password}" /> 
        <!-- 配置初始化大小、最小、最大連接數 --> 
        <property name="initialSize" value="1" /> 
        <property name="minIdle" value="1" /> 
        <property name="maxActive" value="10" />
     </bean>
     
    <!-- 配置事務管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- ********************** 聲明式事務配置 begin ************** -->
    <!-- 配置事務增強的advice -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!-- all methods starting with 'get' are read-only -->
            <tx:method name="get*" read-only="true" />
            <!-- other methods use the default transaction settings (see below) -->
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

    <!-- 配置事務的AOP切面 --> 
    <aop:config>
        <aop:pointcut id="allService" expression="execution(* com.study.leesmall.spring.sample.tx.service.*Service.*(..)))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="allService"/>
    </aop:config>
 <!-- ********************** 聲明式事務配置 end ************** -->
 

 3.1 掌握<tx:method>的各項屬性的配置

 

4. 編程式事務管理

    

  @Autowired
  private PlatformTransactionManager txManager;

public User insertUser(User u) {
        // 1、創建事務定義
        TransactionDefinition definition = new DefaultTransactionDefinition();
        // 2、根據定義開啟事務
        TransactionStatus status = txManager.getTransaction(definition);
        try {
            this.userDao.insert(u);
            // 3、提交事務
            txManager.commit(status);
            return this.userDao.find(u.getId());
        } catch (Exception e) {
            // 4、異常了,回滾事務
            txManager.rollback(status);
            throw e;
        }
    }

 4.1 TransactionTemplate中的代碼示例:

@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 {
            result = action.doInTransaction(status);
        }
        catch (RuntimeException | Error ex) {
            // Transactional code threw application exception -> rollback
            rollbackOnException(status, ex);
            throw ex;
        }
        catch (Throwable ex) {
            // Transactional code threw unexpected exception -> rollback
            rollbackOnException(status, ex);
            throw new UndeclaredThrowableException(ex, "TransactionCallback threw 
            undeclared checked exception");
        }
        this.transactionManager.commit(status);
        return result;
    }
}

四、Spring事務管理API學習

Spring為事務管理提供了統一的抽象建模,這樣我們使用Spring來進行事務管理時,就只需要學會這套API即可,無論底下使用的是何種事務管理方式,jdbc也好,jpa也好,hibernate也好,jta也好。我們的業務代碼中都是面向Spring的事務API。大大降低了我們的學習、使用成本

1.TransactionDefinition

TransactionDefinition:事務定義。Spring事務管理框架將為我們管理事務,但不清楚該如何替我們管理,我們就通過事務定義來定義我們需要的事務管理信息,把這些信給事務管理器,它就知道我們的意圖了。

1.1  來看看它都定義了哪些信息項:

 

 1.2 來看看它有什么實現類可用

 

1.3 看下TransactionAttribute

1.4 請看下DefaultTransactionDefinition、TransactinoTemplate、DefaultTransactionAttribute的源碼

1.5 請把這個繼承體系的類圖畫出來

 

2. PlatformTransactionManager

PlatformTransactionManager平台級的事務管理器,它抽象定義了事務管理行為,不同的事務管理實現實現該接口。我們編程面向該接口。

2.1 看下PlatformTransactionManager中定義的事務管理行為:

 

請仔細看源碼中每個方法的的注釋。

2.2 看下PlatformTransactionManager的子類有哪些:

 

3. TransactionStatus

TransactionStatus 事務狀態,持有事務的狀態信息。事務管理代碼可通過它獲取事務狀態、以及顯式地設置回滾(代替異常的方式)。它繼承了SavePoint接口。在它的實現中會持有事務的很多對象:如事務對象、被掛起的事務資源等等。
從TransactionManager中獲取事務得到它,提交/回滾事務時要給入它:

看 TransactionTemplate 中的使用示例:

 3.1 看下它定義的方法

 

3.2 看它有哪些實現類

 

3.3 看下AbstractTransactionStatus、DefaultTransactionStatus中定義了哪些屬性

 

五、Spring事務源碼學習

 1. AbstractPlatformTransactionManager

 AbstractPlatformTransactionManager是PlatformTransactionManager的實現類

 PlatformTransactionManager對應的源代碼:

PlatformTransactionManager的子類:

 

 

下面來看一下AbstractPlatformTransactionManager中的三個方法的實現邏輯
getTransaction()
commit()
rollback()

 1.1 看一下獲取事物的方法org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(TransactionDefinition)

 

1.1.1 看一下事物已存在時的處理邏輯

 

 

1.1.2 看一下事物掛起時的處理邏輯

 

 1.2 看一下提交事物的方法org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(TransactionStatus)

 

1.3 看一下事物回滾的方法

org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(TransactionStatus)

 

org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(DefaultTransactionStatus, boolean)

總結這里的設計模式、設計原則:
面向接口編程
抽象類:固定不變的實現,提供模板方法,具體子類實現模板方法
一句話:接口、抽象類、模板方法、具體子類。Spring中很多地方用了。

2. DataSourceTransactionManager

 DataSourceTransactionManager是基於jdbc connection的本地事務管理實現。多個方法調用參與到同一個事務,是通過共用connection來完成的

方法一:UserService.insertUser調用了方法二:logService.insertLog(log),兩個都加事務定義,驗證以下幾種傳播:
方法一required---方法二required
方法一required---方法二requires_new
方法一required---方法二nested

2.1 准備

Xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">  
    
    <!-- 加載配置參數 -->
    <context:property-placeholder location="classpath:com/study/leesmall/spring/sample/tx/application.properties"/>
     
    <!-- 數據源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" 
        init-method="init" destroy-method="close"> 
        <property name="driverClassName" value="${jdbc.driverClassName}" /> 
        <property name="url" value="${jdbc.url}" /> 
        <property name="username" value="${jdbc.username}" /> 
        <property name="password" value="${jdbc.password}" /> 
        <!-- 配置初始化大小、最小、最大連接數 --> 
        <property name="initialSize" value="1" /> 
        <property name="minIdle" value="1" /> 
        <property name="maxActive" value="10" />
     </bean>
     
     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" scope="prototype">
         <property name="dataSource" ref="dataSource" />
     </bean>
    
    <!-- *******************  事務管理配置    begin  ********************************** -->
    
    <!-- 配置事務管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- *******************  事務管理配置    end  ********************************** -->
    
</beans>
    
    
    

 UserService:

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    
    @Autowired
    private LogService logService;
    
    @Transactional
    public User insertUser(User u) {
        this.userDao.insert(u);
        Log log = new Log(System.currentTimeMillis() + "", System.currentTimeMillis() + "-" + 
        u.getUserName());
        this.logService.insertLog(log);
        return this.userDao.find(u.getId());
    }
}

LogService:

@Service
public class LogService {
    @Autowired
    private LogDao logDao;
    
    @Transactional
    // @Transactional(propagation = Propagation.REQUIRES_NEW)
    // @Transactional(propagation = Propagation.NESTED)
    public void insertLog(Log log) {
        this.logDao.insert(log);
    }
}

TxMain:

package com.study.leesmall.spring.sample.tx;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.study.leesmall.spring.sample.tx.entity.User;
import com.study.leesmall.spring.sample.tx.service.UserService;

@Configuration
@ComponentScan("com.study.leesmall.spring.sample.tx")
@ImportResource("classpath:com/study/leesmall/spring/sample/tx/application.xml")
@EnableTransactionManagement
public class TxMain {

    public static void main(String[] args) {
        try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxMain.class);) {
            User user = new User();
            user.setId("1234564");
            user.setUserName("leesmall-666666666");

            UserService userService = context.getBean(UserService.class);
            userService.insertUser(user);
        }
    }
}

 在AbstractPlatformTransactionManager.getTransaction方法里面打斷點拿到調用棧去分析

 

接下來跟代碼,重點是要找到 Connection 綁定到線程,綁定到了哪里

調用棧如下:

 

 

看完getTransaction即可。現在已有connection了,接下來業務代碼中使用connection地方

 找到JdbcTemplate的update操作的獲取connection的代碼,加斷點,然后F8,讓程序執行到這里,看下調用棧。

 

 

 

F5進入DataSourceUtils.getConnection(obtainDataSource()),看它如何獲取Connection

調用棧如下:

 

 

 第二次進到 getTransaction()

 

3. 聲明式事務過程源碼學習

先看一下聲明式事務配置的示例:

    <!-- ********************** 聲明式事務配置   ************** -->
    <!-- 配置事務增強的advice -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!-- all methods starting with 'get' are read-only -->
            <tx:method name="get*" read-only="true" />
           <!--  other methods use the default transaction settings (see below) -->
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

    <!-- 配置事務的AOP切面 --> 
    <aop:config>
        <aop:pointcut id="allService" expression="execution(* com.study.leesmall.spring.sample.tx.service.*Service.*(..)))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="allService"/>
    </aop:config> 

3.1 標簽解析

 1)入口

E:\lib\spring-tx-5.1.3.RELEASE.jar

/META-INF/spring.handlers

http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

 

3.2、<tx:advice>標簽的解析器TxAdviceBeanDefinitionParser

a)思考:<tx:advice>advice注冊的會是個什么advice?
b)瀏覽TxAdviceBeanDefinitionParser的代碼,了解<tx:advice><tx:attributes>
<tx:method>的解析過程,產出了什么。
c)瀏覽TransactionInterceptor的代碼,重點:invoke(MethodInvocationinvocation)方法
invokeWithinTransaction方法
d)瀏覽TransactionInterceptor的繼承體系,事務處理的邏輯都在TransactionAspectSupport中
e)瀏覽TransactionAspectSupport
它里面有如下屬性:
它的重點方法是invokeWithinTransaction
f)瀏覽TransactionAttributeSource接口定義
g)瀏覽TransactionAttributeSource的繼承體系
h)再來看下TransactionAttribute,可能已不記得它是什么了

1)解析<tx:advice>標簽

 org.springframework.transaction.config.TxAdviceBeanDefinitionParser.doParse(Element, ParserContext, BeanDefinitionBuilder)

 

org.springframework.transaction.config.TxAdviceBeanDefinitionParser.parseAttributeSource(Element, ParserContext)

 解析<tx:advice id="txAdvice" />標簽的子標簽<tx:attributes>

 

 2)分析TransactionInterceptor 

 TransactionInterceptor的繼承體系:

父類:

子類沒有:

org.springframework.transaction.interceptor.TransactionInterceptor.invoke(MethodInvocation)

 事務處理的邏輯都在TransactionAspectSupport中

瀏覽TransactionAspectSupport
它里面有如下屬性:

它的重點方法是invokeWithinTransaction

 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(Method, Class<?>, InvocationCallback)

 org.springframework.transaction.interceptor.TransactionAspectSupport.getTransactionAttributeSource()

 

 org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(PlatformTransactionManager, TransactionAttribute, String)

 

org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionInfo, Throwable)

 

 3)瀏覽TransactionAttributeSource接口定義

 

TransactionAttributeSource的繼承體系:

3. 事務處理的監聽

事務處理結果的監聽:Spring里面可以對事物處理的結果進行監聽
https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#transaction-event

4. 注解方式事務過程學習

 開啟注解支持:

 <tx:annotation-driven transaction-manager="txManager"/>

 1)入口

E:\lib\spring-tx-5.1.3.RELEASE.jar

/META-INF/spring.handlers

http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser

 org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.parse(Element, ParserContext)

org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer.configureAutoProxyCreator(Element, ParserContext)

 

org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor

 

org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut

 

org.springframework.aop.support.StaticMethodMatcherPointcut

org.springframework.transaction.annotation.AnnotationTransactionAttributeSource

 

什么時候創建代理?

  創建代理:getBean()創建Bean實例的時候

判斷是否要創建代理?

  怎么判斷?Advisor的Pointcut是否匹配。事務是不是一個advisor

怎么使用調用它的方法?
  切面增強、事務

2)開啟注解支持的另外一種方式:

 @EnableTransactionManagement

 2.1)來看@EnableTransactionManagement這個注解的定義

 

@EnableTransactionManagement起作用靠:
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

@Import等價於在TxMain上面導入了一個包

 2.2)看TransactionManagementConfigurationSelector

和<tx:annotation-driven transaction-manager="txManager"/>的實現是一樣的

    /**
     * Returns {@link ProxyTransactionManagementConfiguration} or
     * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
     * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
     * respectively.
     */
    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] {AutoProxyRegistrar.class.getName(),
                        ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {determineTransactionAspectClass()};
            default:
                return null;
        }
    }

 2.3)看org.springframework.context.annotation.AutoProxyRegistrar實現的接口方法:

 

2.4)看org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration類

 

2.5)@import在哪里被解析的
ContextNamespaceHandler
AnnotationConfigBeanDefinitionParser

ConfigurationClassPostProcessor
ConfigurationClassParser
org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass()

org.springframework.context.annotation.ConfigurationClassParser

 

啟動的時候,解析@EnableTransactionManagement,完成事務管理相關的AOP bean注冊
剩下的事情都交給AOP
Xml:
啟動的時候,完成事務管理相關的AOP bean注冊

 

完整代碼獲取地址:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-source-study

官網學習鏈接:
https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#transaction

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM