Spring源码之事务管理

Spring事务管理作为Spring框架提供的一个重要功能,通过AOP的方式提供了通用的事务管理功能,使得程序员可以快速集成和管理应用程序当中的事务。本文将从事务的基本概念、Spring事务核心概念、使用示例、源码分析这几个方面逐步分析Spring事务管理的实现原理,文中涉及的Java相关源码均基于Spring5.3.37版本,SQL相关均基于PostgreSQL 12.8 版本。

1.事务的基本概念

事务的概念在计算机科学领域尤其是数据库管理和分布式系统中非常重要,因为它保证了数据的一致性、可靠性和安全性。

1.1 事务的四大特性(ACID)

事务具备四个基本特性,通常称为 ACID 特性:

  • 原子性(Atomicity):事务中的所有操作作为一个不可分割的工作单元来执行。这意味着要么所有的操作都完成,要么都不完成。如果事务中的某个操作失败,那么整个事务都将被回滚,撤销之前所做的所有更改。
  • 一致性(Consistency):事务的执行结果必须使数据库从一个一致性状态变为另一个一致性状态。即事务完成后,所有的数据必须符合业务规则和约束条件。
  • 隔离性(Isolation):事务的执行是独立的,不会受到其他事务的影响。即一个事务的执行不能被其他事务干扰。数据库系统提供了不同的隔离级别来控制事务之间的交互。
  • 持久性(Durability):一旦事务提交,它对数据库所做的更改是永久的,即使系统发生故障也不会丢失。这意味着一旦事务提交成功,更改将被持久保存在数据库中。

1.2 事务的生命周期

事务从开始到结束经历了一系列的阶段:
开始:事务开始,通常通过调用 BEGIN TRANSACTION 或通过编程语言中的事务管理API开始。
执行:执行一系列数据库操作,包括插入、更新、删除等。
提交:如果所有操作都成功执行并且事务应该被确认,可以调用 COMMIT 或相应的API来提交事务,此时更改将被持久保存。
回滚:如果事务中出现了错误或异常,可以调用 ROLLBACK 或相应的API来回滚事务,撤销之前所做的所有更改。
SQL示例如下:

-- 开启事务,并设置隔离级别为可重复读
begin transaction  ISOLATION LEVEL REPEATABLE READ;
-- 执行操作
insert into t_user(c_id,c_name,c_dept_id,n_valid) values('5','王五', '5', 1);
select c_id,c_name,c_dept_id,n_valid from t_user;
-- 确认操作执行无误,执行提交
commit;
-- 操作执行存在问题,执行回滚
rollback;

2.实现事务的必要信息

从事务的概念中可以知道,要维护一个完整的事务开启执行提交流程,首先需要声明定义事务,如事务的隔离级别等,这样在执行事务中的任务期间,能够根据事务定义获取对应隔离级别的数据,在事务执行结束时,根据事务中任务的情况,进行事务提交或回滚。因此我们要通过代码提取实现整个流程的话,需要维护的基础内容有:
事务信息,即事务定义,涉及事务隔离级别,事务回滚条件等;
事务管理器,管理完整事务的执行流程。
以上描述的是基础流程,在实际情况下,当我们开启一个事务执行相关任务期间,可能会存在需要在内部再开启若干个子任务的情况,若子任务执行需要单独开启事务,则涉及到当前事务的挂起及子事务的开启,此时我们需要一个事务状态,用于标识事务的执行状态,因此引入:
事务状态,维护当前的执行状态;
子任务在代码实现过程会存在方法复用的情况,对于不同的子任务,我们会存在不同事务需求,例如有可能子任务只是单纯大任务的拆分,此时需要确保任务执行的完整性,需要复用主任务的事务,如果与主任务存在差异,会存在需要新开启独立事务的情况,因此还需要有事务传播机制,来标识不同情况。因此引入:
事务传播机制,用于多个任务调用时,标识调用方与被调用方之间事务的使传播。
接下来我们来看看Spring对事务抽象提取了哪些内容。

3.Spring事务核心概念

Spring事务提供了编程式事务和声明式事务,便于程序员灵活自由的使用和管理事务,为了满足两种情况,Spring事务的核心概念可以分为以下几类:

1.事务定义(Transaction Definition)
事务定义描述了事务的行为特征,包括隔离级别、传播行为、只读属性等。
对应Spring中TransactionDefinition接口,对应基础实现类为RuleBasedTransactionAttribute,其UML类图如下:
RuleBasedTransactionAttribute-UML类图

2.事务管理器(Transaction Manager)
事务管理器负责管理事务的生命周期,包括开启事务、提交事务、回滚事务等。
对应Spring中TransactionManager接口,实现该接口的核心接口为PlatformTransactionManager,平台事务管理器接口定义了事务管理的基本操作,包括获取事务、提交事务、回滚事务等。
该接口的核心实现类为DataSourceTransactionManager,用于管理基于JDBC的数据源的事务。

3.事务状态(Transaction Status)
事务状态提供了关于当前事务的信息,包括是否为新事务、是否已提交、是否只读等。
对应Spring中TransactionStatus接口,该接口的默认实现类为DefaultTransactionStatus,提供了关于当前事务的信息。

4.事务传播行为(Propagation Behavior)
事务传播行为定义了事务如何在方法调用之间传播。
对应Spring中的Propagation枚举,枚举值范围如下:
事务传播行为
5.事务隔离级别(Isolation Level)
事务隔离级别定义了事务与其他事务之间的隔离程度。
对应Spring中的Isolation枚举,枚举值范围如下:
事务隔离级别

脏读:脏读发生在一个事务读取了另一个事务未提交的数据时。由于这些数据可能会因为各种原因(如回滚)而被修改或删除,因此,第一个事务读取到的数据是“脏”的,即不一致的或错误的数据。脏读违反了数据库的隔离性。
不可重复读:不可重复读是指在一个事务内,多次读取同一数据集合时,由于其他事务的并发更新,导致在同一个事务内前后两次读取到的数据不一致。这通常发生在读取操作之间有其他事务对数据进行了更新并提交。不可重复读主要影响的是数据的一致性,使得在事务的不同时间点读取到的数据不同。
幻读:发生在当一个事务重新读取一个范围的记录时,另一个并发事务插入了满足该查询条件的新记录,使得第一个事务在后续查询中得到了“幻影”般的额外记录。幻读不仅包括了更新已存在的记录,还包括了插入新的记录。幻读违反了事务的隔离性,因为它允许一个事务在两次查询之间看到其他事务插入的新记录。

4.使用示例

4.1 数据源配置

@Configuration
@EnableTransactionManagement // 开启Spring事务管理
public class DataSourceConfig {
    // 创建数据源
    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource("jdbc:ArteryBase://172.18.13.23:6543/db_test", "tda", "P@ssw0rd");
    }
    // 创建对应数据源的事务管理器
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    // 创建对应数据源的JdbcTemplate
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

4.2 声明式事务

@Service
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    // 使用@Transactional注解标记此方法执行需要启用事务
    @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
    public void addUser() {
        jdbcTemplate.execute("INSERT INTO test.t_user (c_id, c_name, c_dept_id, n_valid) VALUES('1', '张三', '1', 1);");
    }
}

4.3 编程式事务

@Service
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;    
    @Autowired
    private PlatformTransactionManager transactionManager;

    public void addUser() {
        // 创建事务定义
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 设置事务传播行为为需要开启事务
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 设置事务隔离级别为与数据库默认隔离级别保持一致
        def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);

        // 获取事务状态
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            // 执行数据库操作
            jdbcTemplate.execute("INSERT INTO test.t_user (c_id, c_name, c_dept_id, n_valid) VALUES('1', '张三', '1', 1);");
            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            throw e;
        }
    }
}

5.事务启用源码分析

通过示例,我们知道要开启Spring事务管理,需要在配置类上使用@EnableTransactionManagement注解,因此启用的源码分析从此注解入手。

5.1 @EnableTransactionManagement

该注解源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

   boolean proxyTargetClass() default false;

   AdviceMode mode() default AdviceMode.PROXY;

   int order() default Ordered.LOWEST_PRECEDENCE;
}

从源码上看,有个关键注解:@Import(TransactionManagementConfigurationSelector.class),在Spring的BeanDefinition扫描创建注册流程中,我们知道@Import注解会在配置类的扫描流程中被解析,此处@Import注解配置的TransactionManagementConfigurationSelector类由于实现了ImportSelector接口,因此会调用实现自该接口的selectImports(AnnotationMetadata importingClassMetadata)方法得到类名称集合,并将得到的类进一步解析得到所有涉及到的BeanDefintion,其核心源码如下:

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;
   }
}

通过源码可知内部引入了两个类,分别是AutoProxyRegistrar和ProxyTransactionManagementConfiguration。
AutoProxyRegistrar的核心用途是引入InfrastructureAdvisorAutoProxyCreator,用于代理对象的创建,其UML类图如下:
InfrastructureAdvisorAutoProxyCreator-UML类图
其继承了BeanPostProcessor接口,因此会在Bean生命周期的初始化后步骤中被调用,进行代理对象的创建。
ProxyTransactionManagementConfiguration是个配置类,核心用途是引入BeanFactoryTransactionAttributeSourceAdvisor,从名字可以看出引入的类是Advisor,用于对需要开启事务的方法进行拦截处理。内部对应的Pointcut为TransactionAttributeSourcePointcut,用于判断当前正在创建的Bean是否为需要开启事务的Bean;内部对应的Advice是TransactionInterceptor,用于执行具体的事务逻辑,是事务的核心入口类。
因此Spring事务管理是通过AOP的方式实现的,即通过对需要开始事务的目标Bean的方法进行代理,执行对应拦截逻辑完成事务的开启、业务逻辑执行、事务的提交/回滚等操作。

5.2 事务代理对象创建

从前两篇的SpringAOP源码分析中,我们知道要创建代理对象,首先需要找的原始对象对应的Advisor集合,如果存在才会执行代理对象的创建逻辑,整体流程都一致,关键差异在于是否存在符合条件的Advisor、代理逻辑两部分。
在上一节中开启Spring事务管理时,往Spring容器中注册了BeanFactoryTransactionAttributeSourceAdvisor对象,其Pointcut用于判断原始对象是否满足当前切入点条件,Advice则是具体的代理拦截逻辑,因此从这个类的Pointcut属性入手代理对象的创建即可,对应Pointcut的类型为TransactionAttributeSourcePointcut
通过Pointcut判断原始对象是否满足当前切入点条件,需要符合该接口的ClassFilter#matches()和MethodMatcher#matches()两个判断条件,两个条件为递进关系。
先看TransactionAttributeSourcePointcut实现ClassFilter接口的源码:

private class TransactionAttributeSourceClassFilter implements ClassFilter {

   @Override
   public boolean matches(Class<?> clazz) {
       // 如果是内部事务相关的类,直接返回false
      if (TransactionalProxy.class.isAssignableFrom(clazz) ||
            TransactionManager.class.isAssignableFrom(clazz) ||
            PersistenceExceptionTranslator.class.isAssignableFrom(clazz)) {
         return false;
      }
      TransactionAttributeSource tas = getTransactionAttributeSource();
      // 判断当前类是否符合条件
      // 内部有三种解析器,用于解析对应事务注解是否存在,对应关系如下:
      // SpringTransactionAnnotationParser#isCandidateClass() --> org.springframework.transaction.annotation.Transactional
      // JtaTransactionAnnotationParser#isCandidateClass() --> javax.transaction.Transactional
      // Ejb3TransactionAnnotationParser#isCandidateClass() --> javax.ejb.TransactionAttribute
      return (tas == null || tas.isCandidateClass(clazz));
   }
}

TransactionAttributeSourcePointcut实现MethodMatcher接口的源码:

public boolean matches(Method method, Class<?> targetClass) {
   TransactionAttributeSource tas = getTransactionAttributeSource();
   // 判断当前方法是否符合条件
   // 内部有三种解析器,用于解析对应事务注解是否存在,对应关系如下:
   // SpringTransactionAnnotationParser#determineTransactionAttribute() --> org.springframework.transaction.annotation.Transactional
   // JtaTransactionAnnotationParser#determineTransactionAttribute() --> javax.transaction.Transactional
   // Ejb3TransactionAnnotationParser#determineTransactionAttribute() --> javax.ejb.TransactionAttribute
   return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

通过以上内容可知,Spring支持Spring内部提供的Transactional注解,也支持Java自带的Transactional和EJB提供的TransactionAttribute注解,通过判断目标类中是否存在事务注解以及是否有方法上存在事务注解来确认是否需要生成代理对象

6.事务执行源码分析

由于Spring事务管理是通过AOP实现的,因此要分析事务执行流程,需要从实现MethodInterceptor接口的拦截器入手。在正式分析源码之前,先回顾一下事务的通用流程,流程图如下:
在这里插入图片描述
基于这个通用流程,再看一下Spring事务管理的主流程如下:
在这里插入图片描述
可以看到,Spring事务管理的主流程与事务通用流程一致,只不过中间针对不同事务的使用,做了补充。接下来将根据Spring事务管理的主流程,逐步分析内部对应的子流程。
本文分析的是Spring提供的相关事务管理源码流程,因此将以Spring内部核心代码为主流程进行分析。

6.1 解析事务信息

Spring解析事务信息的核心UML时序图如图所示:
Spring解析事务信息-UML时序图

6.2 获取事务管理器

事务管理器的获取来源主要有两处:
①@Transactional注解value属性或transactionManager属性获取事务管理器名称;
②获取全局默认的事务管理器。
由于流程简洁,直接从源码进行分析,源码如下:

protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
   // 如果事务信息或者Bean工厂为空,则取全局默认的事务管理器
   if (txAttr == null || this.beanFactory == null) {
      return getTransactionManager();
   }
   // 从@Transactional注解解析value属性得到的事务管理器Bean名称
   String qualifier = txAttr.getQualifier();
   if (StringUtils.hasText(qualifier)) {
      //从容器中根据类型TransactionManager获取对应事务管理器
      return determineQualifiedTransactionManager(this.beanFactory, qualifier);
   }
   // 如果提前设置了事务管理器Bean名称,则根据设置的名称进行查找
   else if (StringUtils.hasText(this.transactionManagerBeanName)) {
      //从容器中根据类型TransactionManager获取对应事务管理器       
      return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
   }
   else {
      // 取全局默认的事务管理器
      TransactionManager defaultTransactionManager = getTransactionManager();
      if (defaultTransactionManager == null) {
         defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
         if (defaultTransactionManager == null) {
            defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
            this.transactionManagerCache.putIfAbsent(
                  DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
         }
      }
      return defaultTransactionManager;
   }
}

6.3 创建或获取已存在事务

创建或获取已存在的事务主要流程如下UML时序图所示:
Spring创建或获取已存在的事务-UML时序图
从上述流程中可以看出,第10步为事务开启的核心步骤,因此重点分析涉及的源码,其核心步骤如下:
尝试获取已存在的事务;
1.若事务存在,则根据配置的事务传播属性对已存在事务的相关处理;
2.若事务不存在,但配置了事务传播属性,则根据配置开启新事务;
3.若不存在事务且未配置传播属性信息,则不开启事务。
先从入口源码进行分析,源码如下:

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
      throws TransactionException {
   TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
   // 获取事务对象
   Object transaction = doGetTransaction();
   boolean debugEnabled = logger.isDebugEnabled();
   // 根据事务对象判断是否存在事务
   if (isExistingTransaction(transaction)) {
      // 若已存在事务,则根据事务信息里配置的传播属性判断下一步处理动作
      return handleExistingTransaction(def, transaction, debugEnabled);
   }

   // 检查事务超时时间配置是否合理(是否小于-1),不合理则抛出异常
   if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
      throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
   }

   // 事务信息里的传播属性配置值为MANDATORY,由于当前不存在事务,抛出异常
   if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
      throw new IllegalTransactionStateException(
            "No existing transaction found for transaction marked with propagation 'mandatory'");
   }
   // 事务信息里的传播属性配置值为REQUIRED、REQUIRES_NEW、NESTED,由于当前不存在事务,则开启新事务
   else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
         def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
         def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
      // 调用事务挂起方法,此处调用的主要用途是初始化事务同步器属性
      SuspendedResourcesHolder suspendedResources = suspend(null);
      // 省略日志输出代码...
      try {
         // 开启新事务
         return startTransaction(def, transaction, debugEnabled, suspendedResources);
      }
      catch (RuntimeException | Error ex) {
         // 开启新事物失败,则还原挂起的事务
         resume(null, suspendedResources);
         throw ex;
      }
   }
   else {
      // 省略日志输出代码...
      boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
      // 不开启事务,生成一个事务为空的事务状态
      return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
   }
}

6.3.1 开启新事物

Spring事务管理在开启新事物的核心动作是:获取新的数据库连接,并将连接的autoCommit属性设置为false. 开启新事物的入口源码如下:

private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
      boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {

   boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
   // 创建事务状态对象
   DefaultTransactionStatus status = newTransactionStatus(
         definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
   // 核心:开启新连接
   doBegin(transaction, definition);
   // 初始化事务同步状态
   prepareSynchronization(status, definition);
   return status;
}

开启新事物的核心方法doBegin(),核心步骤为:
获取新的数据库连接;
将自动提交设置为false;
将新的数据库连接绑定到当前线程。
其源码如下:

protected void doBegin(Object transaction, TransactionDefinition definition) {
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
   Connection con = null;

   try {
      // 如果当前事务没有连接信息,则需要创建新的连接信息 
      if (!txObject.hasConnectionHolder() ||
            txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
         Connection newCon = obtainDataSource().getConnection();
         // 省略日志输入代码...
         txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
      }
      // 设置事务同步状态为true
      txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
      con = txObject.getConnectionHolder().getConnection();

      // 省略部分代码...

      if (con.getAutoCommit()) {
         // 省略部分代码...
         // 重点!!!:关闭自动提交
         con.setAutoCommit(false);
      }

      // 省略部分代码...

      // 如果是新的连接信息,则将其绑定到当前线程
      if (txObject.isNewConnectionHolder()) {
         TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
      }
   }

   catch (Throwable ex) {
      if (txObject.isNewConnectionHolder()) {
         // 如果是新创建的连接,抛出异常时释放新建的数据库连接
         DataSourceUtils.releaseConnection(con, obtainDataSource());
         txObject.setConnectionHolder(null, false);
      }
      throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
   }
}

6.3.2 处理已存在事务

当执行任务时发现当前线程已存在事务,则会进到此分支逻辑。该方法的核心功能是根据当前事务配置的事务传播行为,执行抛出异常、新建事务等操作,具体直接看源码,源码如下:

private TransactionStatus handleExistingTransaction(
      TransactionDefinition definition, Object transaction, boolean debugEnabled)
      throws TransactionException {
   // 如果事务传播行为配置的是NEVER,若在此已存在事务,则直接抛出异常
   if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
      throw new IllegalTransactionStateException(
            "Existing transaction found for transaction marked with propagation 'never'");
   }
   // 如果事务传播行为配置的是NOT_SUPPORTED,若在此已存在事务,则将事务挂起
   if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
      // 省略日志输出代码...
      // 挂起当前事务
      Object suspendedResources = suspend(transaction);
      boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
      return prepareTransactionStatus(
            definition, null, false, newSynchronization, debugEnabled, suspendedResources);
   }
   // 如果事务传播行为配置的是REQUIRES_NEW,若在此已存在事务,则将当前事务挂起,并开启新的事务
   if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
      // 省略日志输出代码...
      // 挂起当前事务      
      SuspendedResourcesHolder suspendedResources = suspend(transaction);
      try {
         // 开启新的事务
         return startTransaction(definition, transaction, debugEnabled, suspendedResources);
      }
      catch (RuntimeException | Error beginEx) {
         // 开启新事物失败,则需把挂起的事务还原
         resumeAfterBeginException(transaction, suspendedResources, beginEx);
         throw beginEx;
      }
   }
   // 如果事务传播行为配置的是NESTED,若在此已存在事务,则创建一个嵌套事务
   // 嵌套事务有两种实现方式:①如果数据库支持savepoint,则使用此方式实现;②如果数据库不支持savepoint,则采用开启新事务的方式实现(不挂起原事务)。
   if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
      if (!isNestedTransactionAllowed()) {
          // 省略异常信息抛出代码...
      }
      // 省略日志输出代码...
      // 如果数据库支持savepoint,则创建新的savepoint
      if (useSavepointForNestedTransaction()) {
         DefaultTransactionStatus status =
               prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
         status.createAndHoldSavepoint();
         return status;
      }
      else {
         // 如果数据库不支持savepoint,则采用开启新事务的方式
         // 这种需要是支持begin/commit嵌套的数据库才行,并且需要满足:①内层事务失败不影响外层事务;②外层事务失败,内外层事务均需回滚
         // 不挂起旧事务,因为当前新开启的事务与旧事物可以理解为父子关系
         return startTransaction(definition, transaction, debugEnabled, null);
      }
   }

   // 省略日志输出代码...
   // 如果需要进行事务检查,则检查当前事务的隔离级别与配置的事务是否一致;检查当前事务的只读与配置是否一致
   if (isValidateExistingTransaction()) {
      if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
         Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
         if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
            // 省略异常信息抛出代码...
         }
      }
      if (!definition.isReadOnly()) {
         if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
            // 省略异常信息抛出代码...
         }
      }
   }
   boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
   return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}

6.4 挂起/恢复已存在事务

如果执行任务过程中,发现已存在事务,而当前任务配置的事务传播行为为REQUIRES_NEW、NOT_SUPPORTED时,需挂起原事务。如果当前任务执行完成,需要将旧事物还原,即还原已存在的事务。

6.4.1 事务挂起

事务挂起的核心是将旧事物的同步状态、连接信息从当前线程中解绑并构建成挂起资源对象,设置到新开启的事务状态的旧事务属性中暂存,以便于恢复时获取。其源码如下:

protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
    // 如果开启了同步,则需暂存同步器的相关信息
   if (TransactionSynchronizationManager.isSynchronizationActive()) {
      List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
      try {
         Object suspendedResources = null;
         if (transaction != null) {
            // 挂起事务
            // 通用的数据库事务管理类为DataSourceTransactionManager,
            // 其内部逻辑为将连接信息从当前线程中解绑,并将当前事务连接信息设置为空,返回被挂起的数据库连接对象
            suspendedResources = doSuspend(transaction);
         }
         // 省略同步器状态暂存逻辑代码...
         // 构建并返回挂起资源
         return new SuspendedResourcesHolder(
               suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
      }
      catch (RuntimeException | Error ex) {
         // 挂起事务失败,则将旧事物还原
         doResumeSynchronization(suspendedSynchronizations);
         throw ex;
      }
   }
   else if (transaction != null) {
      // 未开启同步器的情况,直接挂起事务
      Object suspendedResources = doSuspend(transaction);
      return new SuspendedResourcesHolder(suspendedResources);
   }
   else {
      // 不存在事务或同步器的情况,不做任何处理
      return null;
   }
}

6.4.2 事务恢复

其核心源码如下:

protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
      throws TransactionException {

   if (resourcesHolder != null) {            
      Object suspendedResources = resourcesHolder.suspendedResources;
      if (suspendedResources != null) {
         // 恢复事务
         // 通用的数据库事务管理类为DataSourceTransactionManager,
         // 其内部逻辑为将连接信息绑定到当前线程中
         doResume(transaction, suspendedResources);
      }
      List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
      // 恢复暂存的同步器相关信息
      if (suspendedSynchronizations != null) {
         TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
         TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
         TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
         TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
         doResumeSynchronization(suspendedSynchronizations);
      }
   }
}

6.5 事务提交

事务提交的核心内容为:根据任务的执行状态,判断是否需要回滚,若需要回滚则调用rollback进行数据回滚;否则调用commit进行数据提交。其核心流程如下UML时序图所示:
Spring事务提交-UML时序图
接下来重点分析其提交processCommit()和回滚的方法processRollback()。

6.3.4.1 processCommit()

事务提交分为两类,一类为使用savepoint控制数据提交,一类为使用commit控制数据提交。其核心源码如下:

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
   try {
      boolean beforeCompletionInvoked = false;

      try {
         boolean unexpectedRollback = false;
         // 扩展口,预处理提交
         prepareForCommit(status);
         // 触发注册的提交前回调
         triggerBeforeCommit(status);
         // 触发注册的完成前回调
         triggerBeforeCompletion(status);
         beforeCompletionInvoked = true;

         if (status.hasSavepoint()) {
            unexpectedRollback = status.isGlobalRollbackOnly();
            // 释放savepoint,完成数据提交,核心方法
            status.releaseHeldSavepoint();
         }
         else if (status.isNewTransaction()) {
            unexpectedRollback = status.isGlobalRollbackOnly();
            // 执行commit,完成数据提交,核心方法
            doCommit(status);
         }
         else if (isFailEarlyOnGlobalRollbackOnly()) {
             // 获取是否需要全局事务回滚(即任务调用链中当任意一个事务失败,关联的事务均需回滚)
            unexpectedRollback = status.isGlobalRollbackOnly();
         }
         // 需要全局事务回滚(即任务调用链中当任意一个事务失败,关联的事务均需回滚),抛出异常,使其他事务进行回滚
         if (unexpectedRollback) {
            throw new UnexpectedRollbackException(
                  "Transaction silently rolled back because it has been marked as rollback-only");
         }
      }
      // 省略部分异常处理代码...
      try {
         // 触发提交完成后的回调
         triggerAfterCommit(status);
      }
      finally {
         // 触发事务完成的回调
         triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
      }

   }
   finally {
       // 清除当前事务绑定到当前线程的相关信息,并还原挂起的事务信息
      cleanupAfterCompletion(status);
   }
}
6.3.4.2 processRollback()

事务回滚分为两类,一类为使用savepoint控制数据回滚,一类为使用commit控制数据回滚。其核心源码如下:

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
   try {
      boolean unexpectedRollback = unexpected;

      try {
         // 触发事务完成前回调
         triggerBeforeCompletion(status);

         if (status.hasSavepoint()) {
            // 如果存在savepoint,则回滚savepoint,使得数据还原,并释放savepoint
            status.rollbackToHeldSavepoint();
         }
         else if (status.isNewTransaction()) {
            // 如果是新事务,则调用rollback进行数据回滚
            doRollback(status);
         }
         else {
            // Participating in larger transaction
            if (status.hasTransaction()) {
               if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                   // 如果当前事务失败或需全局回滚,设置当前事务状态为read-only,即需要回滚
                  doSetRollbackOnly(status);
               }
              // 省略部分代码...
            // Unexpected rollback only matters here if we're asked to fail early
            if (!isFailEarlyOnGlobalRollbackOnly()) {
               unexpectedRollback = false;
            }
         }
      }
      catch (RuntimeException | Error ex) {
         triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
         throw ex;
      }
      // 触发事务完成后回调
      triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

      // Raise UnexpectedRollbackException if we had a global rollback-only marker
      if (unexpectedRollback) {
         throw new UnexpectedRollbackException(
               "Transaction rolled back because it has been marked as rollback-only");
      }
   }
   finally {
      // 清除当前事务绑定到当前线程的相关信息,并还原挂起的事务信息
      cleanupAfterCompletion(status);
   }
}

7.总结

事务作为计算机科学领域尤其是数据库领域的核心功能之一,其核心四大特性为:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。事务的一般流程为:①开启事务;②执行任务;③提交/回滚。
Spring事务管理为了实现通用的事务管理功能,抽象了几个核心概念:事务定义(Transaction Definition)、事务管理器(Transaction Manager)、事务状态(Transaction Status)、事务传播行为(Propagation Behavior)、事务隔离级别(Isolation Level)。
Spring事务管理的核心实现原理为通过AOP的方式,使用事务拦截器 (Transaction Interceptor)对有事务注解标记@Transactional的业务方法进行拦截,进行统一的事务开启、提交/回滚的管理。通过将autocommit设置为false,通过任务执行情况控制执行commit或者rollback
Spring事务管理的事务管理流程与事务的一般流程基本一致,并在此基础上增加了事务挂起/恢复的逻辑,便于管理多个任务互相调用时事务的传播行为。
在使用方式上提供了编程式事务、声明式事务来供程序员灵活选用,以满足不同场景的需求。

  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值