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

1、Spring事務管理的特色

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

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

2、事務概念學習

1. Isolation隔離級別

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

2. Read/Write讀寫

該事務操做是讀、仍是寫、仍是有讀有寫github

3. Timeout超時

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

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。express

幾種事物傳播行爲的狀況:編程

5. SavePoint保存點

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

6. Commit/Rollback提交/回滾

提交、回滾事務app

3、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) {
        // 一、建立事務定義
        TransactionDefinition definition = new DefaultTransactionDefinition();
        // 二、根據定義開啓事務
        TransactionStatus status = txManager.getTransaction(definition);
        try {
            this.userDao.insert(u);
            // 三、提交事務
            txManager.commit(status);
            return this.userDao.find(u.getId());
        } catch (Exception e) {
            // 四、異常了,回滾事務
            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;
    }
}

4、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中定義了哪些屬性

 

5、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.二、<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

相關文章
相關標籤/搜索