文章目录
- 一、前言
- 二、事务的创建 - createTransactionIfNecessary
- 1. 获取事务 - tm.getTransaction(txAttr);
- 2. 构建事务信息 - prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
- 三、总结
一、前言
本文是笔者阅读Spring源码的记录文章,由于本人技术水平有限,在文章中难免出现错误,如有发现,感谢各位指正。在阅读过程中也创建了一些衍生文章,衍生文章的意义是因为自己在看源码的过程中,部分知识点并不了解或者对某些知识点产生了兴趣,所以为了更好的阅读源码,所以开设了衍生篇的文章来更好的对这些知识点进行进一步的学习。
由于 事务的源码和 前篇的 Aop 源码逻辑很类似,所以本篇中某些内容不会展开去讲解,建议先阅读完 全集目录中的Aop部分,再来阅读本文会更好理解。
全集目录:Spring源码分析:全集整理
这是一个巨长的篇章…
全集目录如下:
- Spring源码分析十五:事务实现① - AutoProxyRegistrar
- Spring源码分析十六:事务实现② - ProxyTransactionManagementConfiguration
- Spring源码分析十七:事务实现③ - 事务的创建
- Spring源码分析十八:事务实现④ - 事务的回滚
- Spring源码分析十九:事务实现⑤ - 事务的提交
在 Spring源码分析十六:事务实现② - ProxyTransactionManagementConfiguration 中,解析了关事务的代理创建过程。但是由于篇幅限制,对于事务的创建还没有具体描述,本文来分析一下Spring 事务的创建过程。
需要注意的是:本文虽然叫做事务的创建,但是实际上是 Spring 针对事务开启的一些准备工作(如,事务挂起、事务创建、将事务的提交由自动提交改为手动提交等),真正的事务开启是在Sql 执行时。
首先我们先来了解几个类,以便方便后面分析。
1. TransactionSynchronizationManager
TransactionSynchronizationManager
中使用 ThreadLocal
保存了在不同线程中不同事务的信息。
public abstract class TransactionSynchronizationManager {
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
// 保存当前事务关联的资源--默认只会在新建事务的时候保存当前获取到的DataSource和当前事务对应Connection的映射关系--当然这里Connection被包装为了ConnectionHolder
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
// 事务监听者--在事务执行到某个阶段的过程中,会去回调监听者对应的回调接口(典型观察者模式的应用)---默认为空集合
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
// 存放当前事务名字
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
// 存放当前事务名字只读属性
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");
// 存放当前事务隔离级别
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");
// 存放当前事务是否激活状态
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");
...
}
我们从上面的部分代码可以看到,TransactionSynchronizationManager 中保存的是各个线程中的事务信息。
2. 事务传播属性
如果有不了解 事务传播属性的,也可以先移步观看 事务特性原理及其原理、隔离级别和传播属性,否则后面的判断可能不好理解。
事务传播属性 | 解释 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。即如果上级具有事务,则使用上级的事务,不具备则自己新建一个事务 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。即如果上级存在事务,则挂起上级事务,使用自己新创建的事务 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。即如果上级具有事务,则使用上级的事务,上级没有事务,则抛出异常 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。即如果上级具有事务,则使用上级的事务,如果上级没有事务,则不开启事务 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。即如果上级具有事务,则使用挂起上级事务,使用非事务方式。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 |
这里解释一下 PROPAGATION_NESTED
和 PROPAGATION_REQUIRES_NEW
的区别:
PROPAGATION_REQUIRES_NEW
启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全
commited
或 rolledback
而不依赖于外部事务,它拥有自己的隔离范围, 自己的锁, 等等.
当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
PROPAGATION_REQUIRES_NEW
常用于日志记录,或者交易失败仍需要留痕
另一方面, PROPAGATION_NESTED
开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正
的子事务. 潜套事务开始执行时, 它将取得一个 savepoint.
如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分,
只有外部事务结束后它才会被提交.
由此可见, PROPAGATION_REQUIRES_NEW
和 PROPAGATION_NESTED 的最大区别在于:
PROPAGATION_REQUIRES_NEW
完全是一个新的事务, 而 PROPAGATION_NESTED
则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.
二、事务的创建 - createTransactionIfNecessary
在 Spring源码分析十六:事务实现② - ProxyTransactionManagementConfiguration 中,我们分析到 TransactionAspectSupport#invokeWithinTransaction
方法完成了事务的增强调用。而其中createTransactionIfNecessary 方法则是在需要的时候创建了事务,之所以说需要的时候而不是说直接创建,是因为这里要考虑到事务的传播属性。
createTransactionIfNecessary
的实现是在TransactionAspectSupport#createTransactionIfNecessary
中,完成了事务的创建,这里面考虑了事务的传播属性的处理,所以并不是一定会创建事务,根据传播属性的不同会有不同的处理。
详细代码如下:
// TransactionAttribute 是解析出来的事务
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
// 如果没有名称指定则使用方法唯一标识,并使用 DelegatingTransactionAttribute 封装 txAttr
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 {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 构建事务信息
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
这里我们可以看到其基本逻辑如下:
- 使用
DelegatingTransactionAttribute
封装传入的TransactionAttribute
实例。对于传入的TransactionAttribute
类型的参数txAttr
,当前实际类型是RuleBasedTransactionAttribute
,是由获取事务属性时生成的,主要用于数据承载,而这里之所以使用DelegatingTransactionAttribute
进行封装,也是为了提供更多的功能。 - 获取事务。即
tm.getTransaction(txAttr);
,事务处理的核心当然是事务,这里获取到了事务。实际上getTransaction
方法返回的是TransactionStatus
(实现类是 DefaultTransactionStatus)。DefaultTransactionStatus 是对事务的进一步封装,包含了当前事务信息、挂起事务信息(如果有),保存点等信息。 - 构建事务信息。即
prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
。对上面几个步骤获取的信息构建 TransactionInfo 并返回。TransactionInfo 是 DefaultTransactionStatus 更进一步的封装。
我们来详细看看几个类的具体内容:
-
关于事务管理器
TransactionManager
,不管是JPA还是JDBC等都实现自接口PlatformTransactionManager
如果你添加的是spring-boot-starter-jdbc
依赖,框架会默认注入DataSourceTransactionManager
实例。如果你添加的是spring-boot-starter-data-jpa
依赖,框架会默认注入JpaTransactionManager
实例。 -
TransactionStatus
(实际上的实现是DefaultTransactionStatus
) 里面包含的内容:
这里注意suspendedResources
实际上保存了是挂起的上层事务的信息。如果没有上层事务(也就是没嵌套事务),就是null,这里是通过UserProxyServiceImpl#findAll
(事务传播属性是REQUIRED
) 调用UserServiceImpl#finaAll
(事务传播属性是REQUIRES_NEW
) 的方式挂起来了一个来自com.kingfish.springjdbcdemo.service.UserProxyServiceImpl.findAll
方法的事务信息。savepoint
只有在内嵌事务的隔离级别是PROPAGATION_NESTED
才有可能会保存。 -
TransactionInfo
里面包含的内容:
可以看到 TransactionInfo
是 TransactionStatus
、TransactionAttribute
、TransactionManager
等属性更进一步封装。
- 关于事务挂起封装成的
SuspendedResourcesHolder
。
了解完上述一些类的保存内容后,下面我们来详细分析 createTransactionIfNecessary
中的几个方法
1. 获取事务 - tm.getTransaction(txAttr);
tm.getTransaction(txAttr);
实际上调用的是 AbstractPlatformTransactionManager#getTransaction
方法,在这里面获取了事务(可能是创建新事物,也可能不是),返回的类型是 TransactionStatus。
下面我们来看看其代码:
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// Use defaults if no transaction definition given.
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 1. 获取事务
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 2. 判断当前线程是否存在事务,判断依据是当前线程记录的数据库连接不为空,且连接(connectionHolder)中的 transactionActive 属性 为true;
// 这个方法的实现在 DataSourceTransactionManager#isExistingTransaction。
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 3.当前线程已经存在事务,则按照嵌套事务的逻辑处理
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 到这里就表明当前线程没有事务存在了,即不会出现嵌套事务的情况了
// Check definition settings for new transaction.
// 事务超时验证
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
// 下面是针对事务传播属性进行处理了
// 4. 如果传播属性是 PROPAGATION_MANDATORY 。但是当前线程又不存在事务,则抛出异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// 5. 如果传播属性是PROPAGATION_REQUIRED 、PROPAGATION_REQUIRES_NEW 、PROPAGATION_NESTED 都需要新建事务
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 5.1. 进行空挂起。为了记录原有事务的状态,便于后续操作对事务的恢复。因为这里原先并不存在事务,所以进行空挂起
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 5.2.开启事务,并返回了事务状态
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 6. 构建事务信息
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
可以看到基本逻辑如下:
- 获取事务 : 这里使用的是
DataSourceTransactionManager#doGetTransaction
。创建基于JDBC 的事务实例。如果当前线程存在关于 dataSource 的连接,那么直接使用。这里有对保存点的一个设置,是否开启允许保存点取决于是否设置了允许嵌入式事务。 - 如果当前线程存在事务,则转向嵌套事务的处理
- 进行事务超时设置的验证
- 如果传播属性是
PROPAGATION_MANDATORY
。但是当前线程又不存在事务,则抛出异常 - 如果传播属性是
PROPAGATION_REQUIRED
、PROPAGATION_REQUIRES_NEW
、PROPAGATION_NESTED
都需要新建事务,通过startTransaction
开始事务构建 - 不满足上述传播属性,则使用
prepareTransactionStatus
来进行事务构建
对于一些隔离级别、timeout等功能的设置并不是Spring完成的,而是委托给底层的数据库连接去做的。
基于上面的逻辑我们再展开看一些方法:
1.1 doGetTransaction();
doGetTransaction();
实际调用 DataSourceTransactionManager#doGetTransaction
。目的就是获取事务对象。
其实现逻辑很简单 :如果当前线程存在关于 dataSource
的连接,那么直接使用。这里有对保存点的一个设置,是否开启允许保存点取决于是否设置了允许嵌入式事务。
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 是否允许设置保存点 : 决定是否允许嵌套事务的存在
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 如果当前线程已经记录了数据库连接则使用原有连接
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// false 代表非新创建连接
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
1.2 isExistingTransaction(transaction)
isExistingTransaction(transaction)
的实现是DataSourceTransactionManager#isExistingTransaction
中。其作用是判断当前线程是否存在事务,判断依据是当前线程记录的数据库连接不为空,且连接(connectionHolder
)中的 transactionActive
属性为true
。
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 存在连接,且其 transactionActive 属性不为空,isTransactionActive() 中的返回是 transactionActive = true
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
1.3 handleExistingTransaction(def, transaction, debugEnabled);
handleExistingTransaction(def, transaction, debugEnabled);
的实现在 AbstractPlatformTransactionManager#handleExistingTransaction
中,这里是为了处理嵌套事务,也就是说进入这一步则说明当前线程已经存在了事务。
这里面主要还是根据传播属性的不同而进行不同的逻辑处理。代码如下:
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// 如果传播属性是 PROPAGATION_NEVER,当时当前线程有事务,则抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
// 如果传播属性是 PROPAGATION_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);
}
// 如果传播属性是 PROPAGATION_REQUIRES_NEW,则需要挂起当前事务。新创建事务使用
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// 挂起当前事务,关于 suspend 方法,我们下面会讲
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
// 重新创建事务
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 如果传播属性是 PROPAGATION_NESTED,则如果当前存在事务,则在嵌套事务内执行。否则自己创建事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
// 如果可以使用保存点的方式控制事务回滚,则在嵌入式事务的建立时便建立保存点
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
// 嵌入式事务的建立时便建立保存点
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
// 创建设置保存点
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
// 有些情况下是不能使用保存点操作,如 JTA,这时候就需要新建事务
return startTransaction(definition, transaction, debugEnabled, null);
}
}
// 进行事务的合法性校验
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
需要注意的是:
-
对于
PROPAGATION_REQUIRES_NEW
传播属性,其表示当前方法必须要在他自己的事务中运行,一个新的事务被启动,所以原先的事务会被先挂起(suspend
),挂起后作为当前事务TransactionStatus
的一个属性(suspendedResources
)存在。当当前事务执行结束后,再将原事务还原。 -
对于
PROPAGATION_NESTED
传播属性,由于其需要的并非是全新的事务,而且当前事务的子事务。Spring考虑了两种情况- Spring中允许嵌套事务的时候,则首选设置保存点的方式作为异常处理的回滚。
- 对于其他方式,比如 JTA 无法使用保存点的方式,那么处理方式和
PROPAGATION_REQUIRES_NEW
相同,一旦出现异常,则由spring的事务异常处理机制去完成后续操作。
1.4 suspend(null);
suspend(null);
调用的是 AbstractPlatformTransactionManager#suspend
内容。其主要作用是为了记录原有事务的状态,便于后续操作对事务的恢复,实际上是将原事务的信息封装成 SuspendedResourcesHolder
,作为 TransactionStatus
的一个属性存在。(我们这里的空挂起直接会返回null, 其他情况则会返回一个正常的SuspendedResourcesHolder)。
代码基本逻辑如下:
- 记录原事务信息
- 清除原事务信息
- 将原事务信息封装成
SuspendedResourcesHolder
返回,以便于外界恢复事务时使用
其代码如下:
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
// 如果当前事务处于激活状态
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
// 这里主要将当前事务的数据源(ConnectionHolder)解绑
suspendedResources = doSuspend(transaction);
}
// 获取当前事务name、readOnly、isolationLevel 、wasActive 等属性,封装成 SuspendedResourcesHolder 返回
// 同时将当前事务的各种信息重置
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);
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
catch (RuntimeException | Error ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
// 如果当前事务并未激活且存在transaction
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// Neither transaction nor synchronization active.
return null;
}
}
1.5 startTransaction(def, transaction, debugEnabled, suspendedResources);
startTransaction(def, transaction, debugEnabled, suspendedResources);
的实现在AbstractPlatformTransactionManager#startTransaction
中,主要作用是创建新的事务。
经历了上面几步处理,到达这一步的时候说明,当前已经不是嵌套事务。
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 创建一个默认的DefaultTransactionStatus
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 构造 Transaction,包括设置 ConnectionHolder、隔离级别、timeout。
// 并且如果是新连接,则绑定当当前线程。
doBegin(transaction, definition);
// 新同步事务的设置,针对于当期线程的设置
prepareSynchronization(status, definition);
return status;
}
我们详细看下面两个方法:
1.5.1 doBegin(transaction, definition);
doBegin(transaction, definition);
的实现在 DataSourceTransactionManager#doBegin
中。
这里的目的是 为了构造 transaction,包括设置 ConnectionHolder、隔离级别、timeout。如果是新连接,则绑定到当前线程。
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 如果当前线程中的数据库连接不存在,或者事务同步为true的情况下需要重新获取数据库连接,进行同步
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 将同步标识设置为 true
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// 设置事务隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
// 更改自动提交,将数据库的自动提交改为 Spring 来控制,否则数据库执行结束sql后自动提交了
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 准备事务连接,这里实际上执行了 SET TRANSACTION READ ONLY 的sql 语句
prepareTransactionalConnection(con, definition);
// 设置当前线程已经存在事务,这个 transactionActive 属性是判断是否当前线程存在事务的依据
txObject.getConnectionHolder().setTransactionActive(true);
// 设置超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
// 如果是新的连接,则绑定数据库连接到当前线程
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);
}
}
...
protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)
throws SQLException {
if (isEnforceReadOnly() && definition.isReadOnly()) {
try (Statement stmt = con.createStatement()) {
// 设置事务为只读
stmt.executeUpdate("SET TRANSACTION READ ONLY");
}
}
}
可以说事务是从这个函数开始的,因为在这个函数中已经开始尝试对数据库连接的获取,并在在获取数据库连接的同时,也进行了一些必要的设置的同步。
- 尝试获取连接。但并不是每一次都会获取新的连接,如果当前线程中的 ConnectionHolder 已经存在,则没必要再次获取,或者对事物同步表示设置为true的需要重新获取连接。
- 设置隔离级别和只读标识
- 更改事务的默认提交设置。
如果事务属性是自动提交,那么需要改变这个设置,将操作委托给Spring来处理。 - 设置标志位,标识当前连接已经被事务激活。
- 设置过期时间
- 将
ConnectionHolder
绑定到当前线程
我们这里额外看一下 DataSourceUtils.prepareConnectionForTransaction
方法是如何设置隔离级别的
public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)
throws SQLException {
Assert.notNull(con, "No Connection specified");
boolean debugEnabled = logger.isDebugEnabled();
// Set read-only flag.
// 设置属性只读
if (definition != null && definition.isReadOnly()) {
try {
if (debugEnabled) {
logger.debug("Setting JDBC Connection [" + con + "] read-only");
}
con.setReadOnly(true);
}
catch (SQLException | RuntimeException ex) {
Throwable exToCheck = ex;
while (exToCheck != null) {
if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
// Assume it's a connection timeout that would otherwise get lost: e.g. from JDBC 4.0
throw ex;
}
exToCheck = exToCheck.getCause();
}
// "read-only not supported" SQLException -> ignore, it's just a hint anyway
logger.debug("Could not set JDBC Connection read-only", ex);
}
}
// Apply specific isolation level, if any.
// 设置数据库隔离级别
Integer previousIsolationLevel = null;
if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
if (debugEnabled) {
logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +
definition.getIsolationLevel());
}
int currentIsolation = con.getTransactionIsolation();
if (currentIsolation != definition.getIsolationLevel()) {
previousIsolationLevel = currentIsolation;
con.setTransactionIsolation(definition.getIsolationLevel());
}
}
return previousIsolationLevel;
}
可以看到,在 DataSourceUtils.prepareConnectionForTransaction
方法中并没有什么复杂的逻辑,因为其主要实现都交由更底层的 数据库API 来完成了。
1.5.2 prepareSynchronization(status, definition);
prepareSynchronization(status, definition);
的实现在AbstractPlatformTransactionManager#prepareSynchronization
中。其目的是将事务信息记录到当前线程中,逻辑很简单,这里不再赘述。
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
// 如果是新的事务,则需要同步信息
if (status.isNewSynchronization()) {
// 下面是对事务的信息的记录,记录到当前线程中。
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
2. 构建事务信息 - prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
的实现是在TransactionAspectSupport#prepareTransactionInfo
中。
当已经建立事务连接并完成了事务信息的提取后,我们需要将所有的事务信息统一记录在 TransactionInfo
类型的实例中,这个实例包含了目标方法开始前的所有状态信息,一旦事务执行失败,Spring会通过 TransactionInfo
类实例中的信息来进行回滚等后续工作。
详细代码如下:
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) {
// 记录事务状态
txInfo.newTransactionStatus(status);
}
else {
}
// 绑定到线程当前上
txInfo.bindToThread();
return txInfo;
}
三、总结
TransactionAspectSupport#createTransactionIfNecessary
的功能是根据需要创建事务。这里面考虑到嵌套事务的情况,并对事务的传播属性进行了相应的处理,最终处理后。返回的是一个 TransactionInfo 的值,里面封装了事务的各种信息,供给后面的回滚或者提交使用。
以上:内容部分参考
《Spring源码深度解析》
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正