死磕Spring之AOP篇 - Spring 事务详解 (13)

否则,如果是 REQUIRES_NEW 事务传播级别(无论如何都会创建一个新的事务),因为当前线程正处于一个事务中,此时挂起当前事务,创建一个新的事务

调用 suspend(..) 方法,将当前事务挂起,也就是从 ThreadLocal 中移除各种对象,并返回一个挂起的资源对象(包含所有被移除的对象)

创建一个事务状态对象,设置相关属性,包括被挂起的资源,会设置 newTransaction 为 true,表示是一个新的事务

【关键】调用 doBegin(..) 方法,执行 begin 操作,前面的 1.2 小节已经分析过,不再赘述

借助 TransactionSynchronizationManager 事务同步管理器设置相关 ThreadLocal 变量

返回上面创建的 DefaultTransactionStatus 事务状态对象

否则,如果是 NESTED 事务传播级别(执行一个嵌套事务),还是使用当前线程的事务,不过设置了保存点,相当于一个嵌套事务,在 Mysql 中是采用 SAVEPOINT 来实现的

如果支持使用保存点(默认为 true)

创建一个事务状态对象,设置相关属性,这里设置了 newTransaction 为 false,表示不是一个新的事务

创建一个保存点,调用 Connection#setSavepoint(String) 方法

返回上面创建的 DefaultTransactionStatus 事务状态对象

否则,例如 JtaTransactionManager(JTA 事务),暂时忽略

走到这里了,表示就使用当前已存在的事务,也就是SUPPORTSREQUIRED 两种传播级别

判断是否需要对定义的隔离级别和已存在的事务的隔离级别进行校验(默认为 false)

创建一个 DefaultTransactionStatus 事务状态对象,设置 newTransaction 为 false,表示不是一个新的事务,还是使用当前事务;同时借助 TransactionSynchronizationManager 事务同步管理器设置相关 ThreadLocal 变量;注意这里用的 definition 是当前 @Transactional 注解的相关属性,所以隔离级别等属性是当前定义的,而不是当前已存在的事务的隔离级别

整个处理过程并不复杂,其中挂起当前事务会调用 suspend(..) 方法,我们一起来看看

1.4 suspend 方法

suspend(..) 方法,如果当前正处于一个事务中,传播级别为 NOT_SUPPORTED 或者 REQUIRES_NEW,则需要挂起当前事务,然后不使用事务或者创建一个新的事务,如下:

// AbstractPlatformTransactionManager.java protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException { // <1> 当前线程已有 TransactionSynchronization 事务同步器 if (TransactionSynchronizationManager.isSynchronizationActive()) { // <1.1> 将当前线程的 TransactionSynchronization 全部挂起,也就是从 ThreadLocal 中移除,并返回挂起的对象 List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization(); try { Object suspendedResources = null; if (transaction != null) { // <1.2> 挂起事务,也就是从 ThreadLocal 中移除,并返回挂起的 ConnectionHolder 对象 suspendedResources = doSuspend(transaction); } // <1.3> 解除绑定当前事务各种属性,名称、只读、隔离级别、是否是真实的事务 String name = TransactionSynchronizationManager.getCurrentTransactionName(); TransactionSynchronizationManager.setCurrentTransactionName(null); boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null); boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive(); TransactionSynchronizationManager.setActualTransactionActive(false); // <1.4> 返回被挂起的资源对象(对上面被挂起的对象进行封装) return new SuspendedResourcesHolder( suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive); } catch (RuntimeException | Error ex) { // doSuspend failed - original transaction is still active... // 在抛出异常前唤醒刚才被挂起的资源 doResumeSynchronization(suspendedSynchronizations); throw ex; } } // <2> 否则,如果当前数据源事务对象不为空,则挂起 else if (transaction != null) { // Transaction active but no synchronization active. // <2.1> 挂起事务,也就是从 ThreadLocal 中移除,并返回挂起的 ConnectionHolder 对象 Object suspendedResources = doSuspend(transaction); // <2.2> 返回被挂起的资源对象(对上面被挂起的对象进行封装) return new SuspendedResourcesHolder(suspendedResources); } // <3> 否则,什么都不用做,返回一个空对象 else { // Neither transaction nor synchronization active. return null; } }

挂起当前事务的过程如下:

如果当前线程已有 TransactionSynchronization 事务同步器

将当前线程的 TransactionSynchronization 全部挂起,也就是从 ThreadLocal 中移除,并返回挂起的对象

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygsff.html