SpringBoot源码解读与原理分析(三十四)SpringBoot整合JDBC(三)声明式事务的传播行为控制

文章目录

    • 前言
    • 10.5 声明式事务的传播行为控制
      • 10.5.1 修改测试代码
        • (1)新建一个Service类,并引用UserService
        • (2)修改主启动类
      • 10.5.2 PROPAGATION_REQUIRED
        • 10.5.2.1 tm.getTransaction
          • (1)获取事务
          • (2)当前已有事务时的传播行为处理
          • (3)超时检测
          • (4)当前事务不存在时的传播行为处理
          • (5)开启新事务:```startTransaction```
        • 10.5.2.2 prepareTransactionInfo
        • 10.5.2.3 UserServie.test
        • 10.5.2.4 handleExistingTransaction
          • (1)传播行为PROPAGATION_NEVER的处理
          • (2)传播行为PROPAGATION_NOT_SUPPORTED的处理
          • (3)传播行为PROPAGATION_REQUIRES_NEW的处理
          • (4)传播行为PROPAGATION_NESTED的处理
          • (5)传播行为PROPAGATION_REQUIRED和PROPAGATION_SUPPORTS的处理
      • 10.5.3 PROPAGATION_REQUIRES_NEW
        • 10.5.3.1 挂起原事务:```suspend```
        • 10.5.3.2 开启新事务:```startTransaction```
        • 10.5.3.3 内层新事务执行完成后的处理
    • 10.6 小结

前言

在实际项目开发中,难免遇到Service之间的相互嵌套。如何控制这些嵌套Service的事务,则涉及到SpringFramework的事务传播行为。

10.5 声明式事务的传播行为控制

事务传播行为指的是外层事务传播到内层事务后,内层事务做出的行为。

SpringFramework通过事务传播行为可以控制一个Service中的事务传播到另一个Service方法中的方式。SpringFramework在TransactionDefinition类中定义了7种传播行为:

源码1TransactionDefinition.java

int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;

这7种传播行为的含义分别是:

  • PROPAGATION_REQUIRED:必需的。如果当前没有事务运行,则会开启一个新的事务;如果当前已有事务运行,则方法会运行在当前事务中。
  • PROPAGATION_SUPPORTS:支持。如果当前没有事务运行,则方法也不会运行在事务中;如果当前已有事务运行,则方法也会运行在当前事务中。
  • PROPAGATION_MANDATORY:强制。当前方法必须运行在事务中,如果没有事务则抛出异常。
  • PROPAGATION_REQUIRES_NEW:新事务。如果当前没有事务运行,则会开启一个新的事务;如果当前已有事务运行,则会将该事务挂起(暂停),重新开启一个事务。当新的事务运行完毕后,释放原来的事务。
  • PROPAGATION_NOT_SUPPORTED:不支持。如果当前没有事务运行,则方法也不运行在事务中;如果当前已有事务运行,则会将该事务挂起(暂停)。
  • PROPAGATION_NEVER:不允许。当前方法不允许运行在事务中,如果有事务则抛出异常。
  • PROPAGATION_NESTED:嵌套。如果当前没有事务运行,则会开启一个新的事务;如果当前已有事务运行,则会记录一个保存点,方法继续运行在当前事务中。如果方法出现异常,则不会全部回滚,而是回滚到上一个保存点。

简单总结如下:

REQUIREDSUPPORTSMANDATORYREQUIRES_NEWNOT_SUPPORTEDNEVERNESTED
已有事务运行运行当前事务运行当前事务运行当前事务挂起原事务,开启新事务;新事务运行完后释放原事务挂起原事务,然后不运行事务抛出异常记录保存点,继续运行当前事务,如果出现异常则回滚到保存点
没有事务运行开启新事务不运行事务抛出异常开启新事务不运行事务不运行事务开启新事务

10.5.1 修改测试代码

(1)新建一个Service类,并引用UserService
@Service
public class DeptService {
    
    @Autowired
    private UserService userService;
    
    @Transactional(rollbackFor = Exception.class)
    public void save() {
        System.out.println("DeptService.save 执行了...");
        userService.test();
    }
}

通过引用UserService,实现Service的嵌套。

(2)修改主启动类
@SpringBootApplication
@EnableTransactionManagement
public class JDBCApp {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(JDBCApp.class, args);
        DeptService deptService = context.getBean(DeptService.class);
        deptService.save();
    }

}

10.5.2 PROPAGATION_REQUIRED

默认情况下,标注@Transactional注解对应的事务传播行为是PROPAGATION_REQUIRED,这种传播行为的特征是执行Service方法时必定确保事务的开启。

创建事务的位置是TransactionAspectSupport类的invokeWithinTransaction方法。

源码2TransactionAspectSupport.java

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
                                         final InvocationCallback invocation) throws Throwable {
    // ......

    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 创建事务
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

        //......
    } // else ......
}
源码3TransactionAspectSupport.java

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
                                                       @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

    // 如果事务定义中没有指定name,则将方法名作为事务定义name
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }
    
    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            // 获取事务定义信息对应的事务状态
            status = tm.getTransaction(txAttr);
        } else {
            // logger ...
        }
    }
    // 构建事务信息
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

由 源码3 可知,createTransactionIfNecessary方法有两个核心动作:根据事务定义信息获取事务状态,以及获取事务状态后事务信息构建。

10.5.2.1 tm.getTransaction

事务管理器的getTransaction方法定义在DataSourceTransactionManager的父类AbstractPlatformTransactionManager中。这个方法的篇幅很长,下面拆解来看。

(1)获取事务
源码4AbstractPlatformTransactionManager.java

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {

    // 如果没有给定事务定义信息,则使用默认的
    TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
    // (1)获取事务
    Object transaction = doGetTransaction();
    boolean debugEnabled = logger.isDebugEnabled();
    
    // ......
}

由 源码4 可知,getTransaction方法首先会转调do开头的doGetTransaction方法获取事务。该方法本身是一个模板方法,它的实现定义在DataSourceTransactionManager中。

源码5DataSourceTransactionManager.java

protected Object doGetTransaction() {
    // 创建DataSourceTransactionObject对象
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    txObject.setSavepointAllowed(isNestedTransactionAllowed());
    // 组合ConnectionHolder对象
    ConnectionHolder conHolder =
            (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
    txObject.setConnectionHolder(conHolder, false);
    return txObject;
}

由 源码5 可知,doGetTransaction方法会创建并返回一个DataSourceTransactionObject对象,该对象内部包含了一个ConnectionHolder,而ConnectionHolder内部又组合了一个Connection对象。

换句话说,doGetTransaction方法执行后,返回了一个Connection对象。

(2)当前已有事务时的传播行为处理
源码6AbstractPlatformTransactionManager.java

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {

    // (1)获取事务 ...
    
    // (2)当前已有事务时的传播行为处理
    if (isExistingTransaction(transaction)) {
        return handleExistingTransaction(def, transaction, debugEnabled);
    }
    
    // ......
}

由于在执行DeptService的save方法之前,当前线程还没有开启事务,因此该if判断的结果为false,不会进入handleExistingTransaction方法。

(3)超时检测
源码7AbstractPlatformTransactionManager.java

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {

    // (1)获取事务 ...
    // (2)当前已有事务时的传播行为处理 ...

    // (3)超时检测
    if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
    }

    // ......
}

由 源码7 可知,接着的if结构进行超时校验,实际上就是校验@Transactional注解中的timeout属性

SpringFramework支持的事务模型可以显式设置事务方法最长执行时间(@Transactional注解中的timeout属性),当方法的执行时间超过该时间,则会抛出异常。

默认情况下,timeout属性的值为-1,表示不限制事务方法的执行时间。这里的if结构判断显式设置的timeout属性值小于-1时,说明值本身设置不合理,抛出异常。

(4)当前事务不存在时的传播行为处理
源码8AbstractPlatformTransactionManager.java

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {

    // (1)获取事务 ...
    // (2)外部事务已存在时的逻辑
    // (3)超时检测
    
    // (4)当前事务不存在时的传播行为处理
    if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        // 传播行为:PROPAGATION_MANDATORY
        // 强制要求方法运行在事务中,由于此时线程中没有事务,因此抛出异常
        // throw ...
    } else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
            def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        // 传播行为:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
        // 如果当前线程中没有事务,则开启一个新的事务
        SuspendedResourcesHolder suspendedResources = suspend(null);
        // logger ...
        try {
            // 开启一个新的事务
            return startTransaction(def, transaction, debugEnabled, suspendedResources);
        } // catch ...
    } else {
        // 剩余的传播行为:PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER
        // 如果当前线程没有事务,则方法也不运行在事务中
        // logger ...
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
    }
}

由 源码8 可知,getTransaction方法的第4部分逻辑是当前事务不存在时的传播行为处理。程序进入该部分源码,说明当前线程中没有开启事务,事实上也是如此,源码根据事务定义中配置的事务传播行为分别了做了不同处理。

  • 传播行为是PROPAGATION_MANDATORY时,强制要求方法运行在事务中,由于此时线程中没有事务,因此抛出异常。
  • 传播行为是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED时,如果当前线程中没有事务,则开启一个新的事务(通过startTransaction方法)。
  • 传播行为是PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER时,如果当前线程没有事务,则方法也不运行在事务中,因此不作任何处理。

由于SpringFramework默认的事务传播行为是PROPAGATION_REQUIRED,因此程序会进行else-if结构中,通过startTransaction方法开启一个新事务。

(5)开启新事务:startTransaction
源码9AbstractPlatformTransactionManager.java

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

由 源码9 可知,startTransaction方法的核心动作是调用doBegin方法开启事务,该方法定义在DataSourceTransactionManager类中。

源码10DataSourceTransactionManager.java

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

    try {
        // ......

        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        // 从ConnectionHolder中获取到Connection对象
        con = txObject.getConnectionHolder().getConnection();

        //......
        
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            // 设置autoCommit属性为false,关闭自动提交,开启事务
            con.setAutoCommit(false);
        }

        // ......
    } // catch ...
}

由 源码10 可知,doBegin方法的核心动作是从DataSourceTransactionObject中取出内部组合的ConnectionHolder,再从ConnectionHolder中取出组合的Connection对象,随后执行setAutoCommit(false)方法设置autoCommit属性为false,关闭自动提交,开启事务

10.5.2.2 prepareTransactionInfo

回到 源码3 的createTransactionIfNecessary方法,getTransaction方法执行完毕后,开始执行prepareTransactionInfo方法。

源码11TransactionAspectSupport.java

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
                                                 @Nullable TransactionAttribute txAttr, String joinpointIdentification,
                                                 @Nullable TransactionStatus status) {
    // 创建TransactionInfo对象
    TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
    if (txAttr != null) {
        // logger ...
        // 设置事务信息
        txInfo.newTransactionStatus(status);
    } else {
        // logger ...
    }
    // 绑定到当前线程中
    txInfo.bindToThread();
    return txInfo;
}

由 源码11 可知,prepareTransactionInfo方法会创建一个TransactionInfo对象,把事务状态信息放入其中,并绑定到当前线程中。

当TransactionInfo创建完毕并返回,createTransactionIfNecessary方法执行完毕,事务也就创建好了。

10.5.2.3 UserServie.test

DeptService的save方法的事务开启之后,它的内部会调用UserService的test方法,由于UserServie也是被AOP代理的代理对象,在调用其test方法时会再次进入createTransactionIfNecessary方法处理事务逻辑。

由于此时线程中已经存在事务,因此在getTransaction方法中,会进入到 源码4 的第1个if结构中,即当前已有事务时的传播行为处理(即 源码6 的handleExistingTransaction方法)。

10.5.2.4 handleExistingTransaction
(1)传播行为PROPAGATION_NEVER的处理
源码13AbstractPlatformTransactionManager.java

private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction, boolean debugEnabled)
            throws TransactionException {
        // (1)传播行为PROPAGATION_NEVER的处理
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
            // throw ex...
        }
        // ......
    }

由 源码13 可知,如果是传播行为PROPAGATION_NEVER,要求线程中不能有事务,所以当线程中已有事务时,会直接抛出异常。

(2)传播行为PROPAGATION_NOT_SUPPORTED的处理
源码14AbstractPlatformTransactionManager.java

private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction, boolean debugEnabled)
            throws TransactionException {
        // (1)传播行为PROPAGATION_NEVER的处理...
        
        // (2)传播行为PROPAGATION_NOT_SUPPORTED的处理
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
            // logger...
            // 挂起DeptService的save方法的事务
            Object suspendedResources = suspend(transaction);
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(
                    definition, null, false, newSynchronization, debugEnabled, suspendedResources);
        }
        // ......
    }

由 源码14 可知,如果是传播行为PROPAGATION_NOT_SUPPORTED,要求方法不能执行在事务中,因此此处会把DeptService的save方法的事务挂起,并执行UserService的test方法。

(3)传播行为PROPAGATION_REQUIRES_NEW的处理
源码15AbstractPlatformTransactionManager.java

private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction, boolean debugEnabled)
            throws TransactionException {
        // (1)传播行为PROPAGATION_NEVER的处理...
        // (2)传播行为PROPAGATION_NOT_SUPPORTED的处理...
        
        // (3)传播行为PROPAGATION_REQUIRES_NEW的处理
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
            // logger...
            // 挂起DeptService的save方法的事务
            SuspendedResourcesHolder suspendedResources = suspend(transaction);
            try {
                // 为UserService的```test```方法开启一个新的事务
                return startTransaction(definition, transaction, debugEnabled, suspendedResources);
            } catch (RuntimeException | Error beginEx) {
                // 如果UserService的```test```方法抛出异常,则还原外层事务
                resumeAfterBeginException(transaction, suspendedResources, beginEx);
                throw beginEx;
            }
        }
        // ......
    }

如果传播行为是PROPAGATION_REQUIRES_NEW,会挂起之前的事务,并开启一个新的事务。

由 源码15 可知,此处会把DeptService的save方法的事务挂起,并为UserService的test方法开启一个新的事务。同时,如果UserService的test方法抛出异常,则还原外层事务。

(4)传播行为PROPAGATION_NESTED的处理
源码16AbstractPlatformTransactionManager.java

private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction, boolean debugEnabled)
            throws TransactionException {

        // (1)传播行为PROPAGATION_NEVER的处理...
        // (2)传播行为PROPAGATION_NOT_SUPPORTED的处理...
        // (3)传播行为PROPAGATION_REQUIRES_NEW的处理...

        // (4)传播行为PROPAGATION_NESTED的处理
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            // 检查是否允许嵌套事务
            if (!isNestedTransactionAllowed()) {
                // throw ex...
            }
            // logger...
            // 检查是否支持保存点
            if (useSavepointForNestedTransaction()) {
                DefaultTransactionStatus status =
                        prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
                status.createAndHoldSavepoint();
                return status;
            } else {
                // 如果不支持创建保存点,则会开启一个新的事务
                return startTransaction(definition, transaction, debugEnabled, null);
            }
        }

        // ......
    }

如果传播行为是PROPAGATION_NESTED,则会记录一个保存点,方法继续运行在当前事务上。如果出现异常,不会全部回滚,而是回滚到上一个保存点。

由 源码16 可知,处理逻辑首先会检查是否允许嵌套事务以及是否支持保存点,如果两个条件都成立,则会创建保存点,UserService的test方法继续运行在当前事务中。

如果不支持创建保存点,则会调用startTransaction方法开启一个新的事务,相当于降级到了传播行为PROPAGATION_REQUIRES_NEW。

(5)传播行为PROPAGATION_REQUIRED和PROPAGATION_SUPPORTS的处理
源码17AbstractPlatformTransactionManager.java

private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction, boolean debugEnabled)
            throws TransactionException {

        // (1)传播行为PROPAGATION_NEVER的处理...
        // (2)传播行为PROPAGATION_NOT_SUPPORTED的处理...
        // (3)传播行为PROPAGATION_REQUIRES_NEW的处理...
        // (4)传播行为PROPAGATION_NESTED的处理...
        
        // (5)传播行为PROPAGATION_REQUIRED和PROPAGATION_SUPPORTS的处理
        if (isValidateExistingTransaction()) {
            // ...
        }
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
    }

如果程序没有进入(1)-(4)的处理,则会进入最后的处理逻辑,即传播行为PROPAGATION_REQUIRED和PROPAGATION_SUPPORTS的处理。

由 源码17 可知,由于isValidateExistingTransaction方法默认返回false,因此不会进入if结构,而最后的两行源码是把现有的事务信息封装并返回。

10.5.3 PROPAGATION_REQUIRES_NEW

将UserService的test方法上的@Transactional注解的事务传播行为改为PROPAGATION_REQUIRES_NEW,并重新执行主启动类。

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void test() {

由 10.5.2 的分析可知,该传播行为下会进入传播行为PROPAGATION_REQUIRES_NEW的处理逻辑,在handleExistingTransaction方法中挂起之前的事务,并开启一个新的事务,即 源码15 中的逻辑。

10.5.3.1 挂起原事务:suspend
源码18AbstractPlatformTransactionManager.java

protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
        try {
            Object suspendedResources = null;
            if (transaction != null) {
                // 事务和同步均开启,调用doSuspend方法
                suspendedResources = doSuspend(transaction);
            }
            // ...
        } // catch ...
    } else if (transaction != null) {
        // 事务开启,但同步未开启
        // 调用doSuspend方法
        Object suspendedResources = doSuspend(transaction);
        return new SuspendedResourcesHolder(suspendedResources);
    } else {
        // 事务和同步均未开启
        return null;
    }
}

由 源码18 可知,suspend方法会检查事务和事务同步是否已开启,如果事务开启了,不管同步是否开启,最终都会调用doSuspend方法真正挂起事务。doSuspend的实现在DataSourceTransactionManager类中。

源码19DataSourceTransactionManager.java

@Override
protected Object doSuspend(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    txObject.setConnectionHolder(null);
    return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}

由 源码19 可知,doSuspend方法将DataSourceTransactionObject对象中的ConnectionHolder设置为null,即移除掉ConnectionHolder对象,然后解除事务同步管理器TransactionSynchronizationManager中的资源绑定。

通过Debug发现,doSuspend方法返回的是组合了Connection对象的ConnectionHolder对象。

doSuspend方法返回ConnectionHolder对象
随后,suspend继续执行,doSuspend方法返回的ConnectionHolder对象被封装成SuspendedResourcesHolder对象并返回。

10.5.3.2 开启新事务:startTransaction
源码20AbstractPlatformTransactionManager.java

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

    boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    // SuspendedResourcesHolder对象被存入TransactionStatus
    DefaultTransactionStatus status = newTransactionStatus(
            definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
    // 开启新事务
    doBegin(transaction, definition);
    prepareSynchronization(status, definition);
    return status;
}

由 源码20 可知,startTransaction方法的最后一个参数就是上一步返回的SuspendedResourcesHolder对象,该对象被存入了TransactionStatus中,随后调用doBegin方法开启新事务。

这样做的目的是,当内层新事务执行完成后清理相关的线程同步等信息时,可以从事务状态信息中提取出被挂起的事务,然后恢复外层事务

10.5.3.3 内层新事务执行完成后的处理

在 SpringBoot源码解读与原理分析(三十三)SpringBoot整合JDBC(二)声明式事务的生效原理和控制流程 10.4.2.5 事务执行的后处理 中提到,无论是成功提交事务(processCommit方法)还是回滚事务(processRollback方法),最终都会执行一个cleanupAfterCompletion方法。

源码21AbstractPlatformTransactionManager.java

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    status.setCompleted();
    // 组件资源清除
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.clear();
    }
    if (status.isNewTransaction()) {
        doCleanupAfterCompletion(status.getTransaction());
    }
    // 释放挂起的事务
    if (status.getSuspendedResources() != null) {
        // logger ...
        Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
        resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
    }
}

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

    if (resourcesHolder != null) {
        // 取出其中的ConnectionHolder对象
        Object suspendedResources = resourcesHolder.suspendedResources;
        if (suspendedResources != null) {
            doResume(transaction, suspendedResources);
        }
        // ......
    }
}

由 源码21 可知,前两个if结构与组件资源清除相关,最后一个if结构用于释放挂起的事务。在resume方法中,取出SuspendedResourcesHolder对象中封装的ConnectionHolder对象,并调用doResume方法真正地释放事务。

源码22DataSourceTransactionManager.java

@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
    TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}

由 源码22 可知,doResume方法会将ConnectionHolder对象重新绑定到事务同步管理器TransactionSynchronizationManager中。

可以发现,doResume方法的逻辑和doSuspend方法的逻辑是相反的,doResume方法中的是bindResource方法,而doSuspend方法中的是unbindResource方法。

resume方法执行完毕,原来的外层事务又重新被绑定到线程上,相当于恢复了被挂起之前的状态。

10.6 小结

第10章到此就梳理完毕了,本章的主题是:SpringBoot整合JDBC。回顾一下本章的梳理的内容:

(三十二)SpringBoot整合JDBC(一)JDBC组件的自动装配
(三十二)SpringBoot整合JDBC(二)声明式事务的生效原理和控制流程
(三十四)SpringBoot整合JDBC(三)声明式事务的传播行为控制

更多内容请查阅分类专栏:SpringBoot源码解读与原理分析

第11章主要梳理:SpringBoot整合MyBatis。主要内容包括:

  • MyBatis框架概述与工程整合;
  • SpringBoot整合MyBatis的核心组件自动装配。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/411381.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IO进程线程作业day7

信号灯集共享内存 自定义头文件 #ifndef SEM_H_ #define SEM_H_ //创建信号灯集, int creat_t(int number); //申请释放资源 int P(int semid,int semno); //申请释放资源 int V(int semid,int semno); //删除信号灯集 int del(int semid); #endif信号灯集函数集合 #include…

物资管理新篇章:Java+SpringBoot实战

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

基于 ResNet50和 SVM + 决策树的人脸口罩检测

欢迎收看&#xff0c;这篇文章是关于我如何使用 ResNet50作为特征提取器来构建掩码检测&#xff0c;然后使用支持向量机(SVM) 决策树和叠加集成方法作为分类器的一个快速解释。 为了向研究人员致敬&#xff0c;这个应用程序是基于研究论文&#xff0c;题目是“在2019冠状病毒…

分布式应用:kylin 部署 zabbix 监控平台

目录 一、实验 1.环境 2. kylin 修改mysql数据库 3. kylin 部署 zabbix 监控平台 4. kylin 修改 zabbix 配置 5. kylin 修改zabbix web 二、问题 1. zabbix_server 查看版本报错 2.zabbix_server 文件如何去掉注释"#"和空行 3. zabbix图表显示异常 4.zabbi…

前端常见面试题之vue3

文章目录 1. vue3比vue2有哪些优势2. 描述vue3的生命周期3. 如何看待vue3中的Composition API 和 Options API4. 如何理解ref、 toRef、和toRefs?5. vue3升级了哪些功能6. Composition API如何实现代码逻辑的复用&#xff08;hook)7. Vue3如何实现响应式的8.Vue3使用Proxy对象…

2-22 方法、面向对象、类、JVM内存、构造方法

文章目录 方法的重载面向对象类、属性和方法成员变量默认值属性JVM简单内存分析栈空间堆空间 构造方法执行过程构造器注意点 方法的重载 一个类中名称相同&#xff0c;但是参数列表不同的方法 参数列表不同是指&#xff1a; 形参类型形参个数形参顺序 面向对象 field —— …

linux系统---防火墙拓展

目录 一、iptables 1.基本语法 2.四表五链——重点记忆 2.1四表 2.2五链 2.3总结 3.iptables选项示例 3.1 -Z 清空流量计数 3.2 -P 修改默认规则 3.3 -D 删除规则 3.4 -R 指定编号替换规则 4.白名单 5.通用匹配 6.示例 6.1添加回环网卡 6.2可以访问端口 6.3 主…

架构设计实践:熟悉架构设计方法论,并动手绘制架构设计图

文章目录 一、架构设计要素1、架构设计目标2、架构设计模式&#xff08;1&#xff09;分而治之&#xff08;2&#xff09;迭代式设计 3、架构设计的输入&#xff08;1&#xff09;概览&#xff08;2&#xff09;功能需求 - WH分析法&#xff08;3&#xff09;质量 - “怎么”分…

都说了别用BeanUtils.copyProperties,这不翻车了吧

分享是最有效的学习方式。 博客&#xff1a;https://blog.ktdaddy.com/ 故事 新年新气象&#xff0c;小猫也是踏上了新年新征程&#xff0c;自从小猫按照老猫给的建议【系统梳理大法】完完整整地梳理完毕系统之后&#xff0c;小猫对整个系统的把控可谓又是上到可一个新的高度。…

Arduino中安装ESP32网络抽风无法下载 暴力解决办法 python

不知道什么仙人设计的arduino连接网络部分&#xff0c;死活下不下来。&#xff08;真的沙口&#xff0c;第一次看到这么抽风的下载口&#xff09; 操作 给爷惹火了我踏马解析json选zip直接全部下下来 把这个大家的开发板管理地址下下来跟后面python放在同一目录下&#xff0c…

【Java程序设计】【C00317】基于Springboot的智慧社区居家养老健康管理系统(有论文)

基于Springboot的智慧社区居家养老健康管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的智慧社区居家养老健康管理系统设计与实现&#xff0c;本系统有管理员、社区工作人员、医生以及家属四种角色权限 管…

理解这几个安全漏洞,你也能做安全测试!

如今安全问题显得越来越重要&#xff0c;一个大型的互联网站点&#xff0c;你如果每天查看日志&#xff0c;会发现有很多尝试攻击性的脚本。 如果没有&#xff0c;证明网站影响力还不够大。信息一体化的背后深藏着各类安全隐患&#xff0c;例如由于开发人员的不严谨导致为Web应…

基于24扇区细分的三电平逆变器异步电机直接转矩控制系统学习

导读&#xff1a;本期文章介绍异步电机三电平24扇区的直接转矩控制。三电平逆变器直接转矩控制中&#xff0c;传统的PWM控制方法存在错判区间等问题。本文在借鉴三电平逆变器单一矢量及合成矢量的直接转矩控制研究和两电平12扇区直接转矩控制的基础上&#xff0c;将两电平12扇区…

堆/堆排序(C/C++)

本篇文章将会较为全面的介绍堆的概念以及实现堆两个重要算法&#xff1a;向上调整算法和向下调整算法。接着实现了堆排序。 若想查看对应位置&#xff0c;可直接按照以下目录进行查看&#xff1a; 目录 1.堆的概念及结构 2.堆的实现 2.1 堆的向上调整算法 2.2 堆的向下调整算法…

beego代理前端web的bug

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、beego代理前端web的bug总结 一、beego代理前端web的bug *报错&#xff0c;为web压缩包index.html里面的注释被错误解析&#xff0c;删掉就行 2024/02/22 10:2…

解析Hadoop三大核心组件:HDFS、MapReduce和YARN

目录 HadoopHadoop的优势 Hadoop的组成HDFS架构设计Yarn架构设计MapReduce架构设计 总结 在大数据时代&#xff0c;Hadoop作为一种开源的分布式计算框架&#xff0c;已经成为处理大规模数据的首选工具。它采用了分布式存储和计算的方式&#xff0c;能够高效地处理海量数据。Had…

蛇形矩阵1

题目描述 把数1&#xff0c;2&#xff0c;3&#xff0c;…&#xff0c;N*N按照“蛇形1”放入N*N的矩形中&#xff0c;输出结果。 下面是N10的蛇形1的图示 输入格式 第一行1个正整数&#xff1a;N&#xff0c;范围在[1,100]。 输出格式 N行&#xff0c;每行N个整数。 输入/…

docker下gitlab安装配置

一、安装及配置 1.gitlab镜像拉取 docker pull gitlab/gitlab-ce:latest2.运行gitlab镜像 docker run -d -p 443:443 -p 80:80 -p 222:22 --name gitlab --restart always --privilegedtrue -v /home/gitlab/config:/etc/gitlab -v /home/gitlab/logs:/var/log/gitlab -v …

小家电—简易过零检测电路

趁刚开工时间有空&#xff0c;总结分析下&#xff0c;在工作项目中常用过零检测电路。 图一 图二 图一在项目中较为常用&#xff0c;两个电路都是通过钳位二极管限幅产生过零脉冲信号。 过零信号高电平被钳位在5.7V&#xff0c;低电平为-0.7V 高电平&#xff1a;VCC0.7V 低电…

LeetCode第七题: 整数反转

题目描述 给你一个 32 位的有符号整数 x​ &#xff0c;返回将 x​ 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1]​ &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例 …