基于最新Spring 5.x,详细介绍了Spring 事务源码,包括createTransactionIfNecessary方法,该方法尝试创建并处理事务,是核心方法。
此前的文章中,我们介绍了Spring AOP中最重要的类之一——TransactionInterceptor事务拦截器的工作总体流程,以及invoke方法的前半部分源码。现在我们继续向后看,我们来看看最重要的createTransactionIfNecessary
方法的源码。
Spring 事务源码 系列文章
Spring 事务源码(1)—<tx:advice/>事务标签源码解析
Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析
Spring 事务源码(3)—@EnableTransactionManagement事务注解源码解析
Spring 事务源码(4)—BeanFactoryTransactionAttributeSourceAdvisor注解事务通知器源码解析
Spring 事务源码(5)—TransactionInterceptor事务拦截器与事务的AOP增强实现
Spring 事务源码(6)—createTransactionIfNecessary处理事务属性并尝试创建事务【两万字】
Spring 事务源码(7)—事务的completeTransactionAfterThrowing回滚、commitTransactionAfterReturning提交以及事务源码总结【一万字】
文章目录
- Spring 事务源码 系列文章
- 1 createTransactionIfNecessary尝试创建并开启事务
- 2 TransactionInfo事务信息
- 3 getTransaction获取/开启事务
- 4 小结
1 createTransactionIfNecessary尝试创建并开启事务
如有必要,根据给定的事务属性、事务管理器、方法连接点描述字符串(全限定方法名)信息创建一个事务信息对象TransactionInfo并返回。
TransactionInfo内部的TransactionStatus类型的属性就表示当前线程在当前方法创建的事务,该方法执行完毕之后,可能开启了新事物。TransactionStatus我们在此前学习Spring事务的部分就介绍过了,它就表示一个用于当前方法的事务,如果方法没有应用自己的事务,那么它的TransactionInfo对应的TransactionStatus属性为null!
最后,会将当前的TransactionInfo对象绑定到拦截器的transactionInfoHolder属性中,该属性是一个ThreadLocal,因此这个TransactionInfo实际上就是绑定到当前线程的线程栈中,这样从transactionInfoHolder中获取的TransactionInfo就是最新的方法的事务,而此前的方法的事务信息则是通过TransactionInfo的oldTransactionInfo属性来获取的,这样就形成了一个不同方法的事务信息的链表!
/**
* TransactionAspectSupport的方法
* <p>
* 如有必要,根据给定的TransactionAttribute创建一个事务。
*
* @param txAttr TransactionAttribute,可能为null
* @param joinpointIdentification 完全限定的方法名称,用于记录日志
* @return 一个TransactionInfo对象,无论是否创建了一个事务。TransactionInfo上的hasTransaction()方法可用于判断是否创建了事务。
*/
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
/*如果未指定事务名称,则将方法标识(方法全限定名)用作事务名称。*/
if (txAttr != null && txAttr.getName() == null) {
//创建一个DelegatingTransactionAttribute,getName将返回方法全限定名
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
/*
* 通过事务属性从事务管理器中获取一个事务状态,实际上TransactionStatus就可以看作一个事务
* 该方法非常重要,这里就表示为当前方法获取了一个事务
*/
status = tm.getTransaction(txAttr);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
//准备一个TransactionInfo
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
/**
* TransactionAspectSupport的方法
* <p>
* 为给定的属性和状态对象准备一个TransactionInfo
*
* @param txAttr TransactionAttribute,可能为null
* @param joinpointIdentification 完全限定的方法名称,用于记录日志
* @param status 当前事务的TransactionStatus
* @return 准备好的TransactionInfo对象
*/
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
/*
* 根据给定的事务属性、事务管理器、方法连接点描述字符串(全限定方法名)信息创建一个事务信息对象TransactionInfo
*/
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
// 如果事务属性不为null
if (txAttr != null) {
// We need a transaction for this method...
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
//那么设置事务状态,这里就表示为当前方法创建了一个事务
txInfo.newTransactionStatus(status);
}
// 如果事务属性为null,那么表示当前方法必须要创建事务
else {
if (logger.isTraceEnabled()) {
logger.trace("No need to create transaction for [" + joinpointIdentification +
"]: This method is not transactional.");
}
}
//始终将最新的TransactionInfo绑定到当前线程,即使我们没有在此处创建新的事务也是如此。
//也就是将当前线程的最新事务栈设置为当前对象存入transactionInfoHolder中
//这保证即使此方面未创建任何事务,也将正确管理TransactionInfo堆栈。
txInfo.bindToThread();
return txInfo;
}
2 TransactionInfo事务信息
TransactionInfo是TransactionAspectSupport的内部类,用来保存线程的执行方法时的事务信息。
内部保存了事务管理器transactionManager、事务属性transactionAttribute、全路径方法名joinpointIdentification。还保存了当前方法的事务transactionStatus,以及前一个方法的事务信息对象oldTransactionInfo。
/**
* TransactionAspectSupport的属性
* <p>
* 当前线程的TransactionInfo对象
* <p>
* 如果切面涉及多个单一方法(例如,around advice),
* 则holder应支持currentTransactionStatus()方法,并支持不同合作通知之间的通信(例如,before 和 after advice)。
*/
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
new NamedThreadLocal<>("Current aspect-driven transaction");
/**
* TransactionAspectSupport的内部类
* <p>
* 用来保存某个方法的事务信息的内部对象
*/
protected static final class TransactionInfo {
/**
* 事务管理器
*/
@Nullable
private final PlatformTransactionManager transactionManager;
/**
* 事务属性
*/
@Nullable
private final TransactionAttribute transactionAttribute;
/**
* 连接点描述字符串(方法的全限定名),主要用于记录日志
*/
private final String joinpointIdentification;
/**
* 事务状态
*/
@Nullable
private TransactionStatus transactionStatus;
/**
* 老的事务信息对象
*/
@Nullable
private TransactionInfo oldTransactionInfo;
public TransactionInfo(@Nullable PlatformTransactionManager transactionManager,
@Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) {
this.transactionManager = transactionManager;
this.transactionAttribute = transactionAttribute;
this.joinpointIdentification = joinpointIdentification;
}
public PlatformTransactionManager getTransactionManager() {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
return this.transactionManager;
}
@Nullable
public TransactionAttribute getTransactionAttribute() {
return this.transactionAttribute;
}
/**
* 返回此连接点的String表示形式(通常是方法调用),以用于日志记录。
*/
public String getJoinpointIdentification() {
return this.joinpointIdentification;
}
public void newTransactionStatus(@Nullable TransactionStatus status) {
this.transactionStatus = status;
}
@Nullable
public TransactionStatus getTransactionStatus() {
return this.transactionStatus;
}
/**
* 返回是否是通过此切面创建的事务,或者是否只是一个占位符以保持ThreadLocal堆栈完整性。即当前方法是否新建了事务。
*/
public boolean hasTransaction() {
return (this.transactionStatus != null);
}
private void bindToThread() {
//向线程公开当前的TransactionStatus,并保留所有现有的老的TransactionStatus,以在此事务完成后恢复。
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
private void restoreThreadLocalStatus() {
// 使用堆栈还原旧的事务TransactionInfo。如果未设置,则为null。
transactionInfoHolder.set(this.oldTransactionInfo);
}
@Override
public String toString() {
return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction");
}
}
3 getTransaction获取/开启事务
在createTransactionIfNecessary
方法中,如果存在事务属性TransactionAttribute,并且存在事务管理器PlatformTransactionManager,那么将调用事务管理器的getTransaction方法根据为当前方法配置的事务定义的属性尝试获取事务,将返回一个TransactionStatus对象。
该方法是事务管理的核心方法,其骨干实现位于抽象实现类AbstractPlatformTransactionManager
中,该方法根据配置的各种事务传播行为做出不同的处理,方法执行完毕将可能开启了新事物,也可能没有开启,甚至抛出异常。AbstractPlatformTransactionManager的设计基于模版方法模式,他提供了处理流程,并且提供了一系列的do……模版方法供子类来实现自己的逻辑。
getTransaction方法的大概逻辑为:
- 首先通过
doGetTransaction
方法获取一个内部事务对象
,该方法由具体的事务管理器子类自己实现,返回的对象也是不一样的,对于DataSourceTransactionManager
这里获取的实际上是一个DataSourceTransactionObject
,其内部可能会包含当前数据的连接以及当前的事务信息,也可能没有(比如首次进入事务方法)。 - 通过
isExistingTransaction
方法判断事物对象内部的数据库连接connectionHolder是否不为null并且是否已经开启过了事务。- 如果条件都满足,那么表示此前已经开启过了事务,即存在外层事务,随后调用
handleExistingTransaction
方法统一处理这种情况,比如加入当前事务、新建事务、抛出异常等等逻辑。
- 如果条件都满足,那么表示此前已经开启过了事务,即存在外层事务,随后调用
- 如果不都满足,即不存在外层事务,那么说明当前的事务方法就是最外层的事务方法,那么走下面的逻辑。
- 校验为当前事务方法设置的事务
超时时间
,如果小于默认超时时间(-1),将会抛出异常。 - 如果为当前事务方法设置的
传播行为是
PROPAGATION_MANDATORY`,该传播行为的含义是:如果当前存在事务,则当前方法加入到该事务中去,如果当前不存在事务,则当前方法直接抛出异常。这里由于不存在外层事务,那么这里就直接抛出异常:“No existing transaction found for transaction marked with propagation ‘mandatory’”。 - 如果为当前事务方法设置的
传播行为是PROPAGATION_REQUIRED或者PROPAGATION_REQUIRES_NEW或者PROPAGATION_NESTED
,这些传播行为的含义的共同点之一就是:如果当前不存在事务,就创建一个新事务运行。这里由于不存在外层事务,那么这里就直接创建一个新事物。- 首先调用
suspend
方法首先挂起事务同步,然后再委派给doSuspend模板方法挂起事务,将返回被挂起的资源,用于后续恢复,如果没有事务同步也没有事务,那么将返回null。这里由于没有已存在的事务,那么参数传递null,一般也会将返回null。 - 调用
startTransaction
方法,内部依次调用newTransactionStatus方法创建TransactionStatus、doBegin方法真正的开启新的事务、prepareSynchronization方法准备事务同步
,最终返回TransactionStatus,该对象包含了创建的内部事务对象,以及其他事务信息。
- 首先调用
- 否则,配置的
事务的传播行为就是剩下的三种:PROPAGATION_SUPPORTS或PROPAGATION_NEVER或PROPAGATION_NOT_SUPPORTED
,这几个传播行为的含义的共同点之一就是:当前方法一定以非事务的方式运行。那么这里仍然会创建TransactionStatus对象,但是不会开启事物,相当于一个空事务。- 调用
prepareTransactionStatus
方法返回一个TransactionStatus,和上面的startTransaction方法相比,其内部会调用newTransactionStatus和prepareSynchronization,但不会调用doBegin方法
,因此不会真正的开启事物。这里它的newTransaction参数为false,suspendedResources参数为null。
- 调用
- 校验为当前事务方法设置的事务
/**
1. AbstractPlatformTransactionManager的实现
2. <p>
3. 此根据事务的传播行为做出不同的处理
4. 5. @param definition TransactionDefinition实例,描述传播行为,隔离级别,超时时间等(默认情况下可以为null)。
*/
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
//如果未提供事务定义,那么使用一个默认事务定义对象StaticTransactionDefinition,这是单例模式的应用。
//该默认事务定义对象具有默认的事务定义属性,默认值就是TransactionDefinition接口中定义的默认方法返回值
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
/*
* 获取当前事务,该方法由具体的事务管理器子类自己实现,比如DataSourceTransactionManager、JtaTransactionManager
* 一般都使用DataSourceTransactionManager这个事务管理器
*
* 对于DataSourceTransactionManager这里获取的实际上是一个数据库的事务连接对象,即DataSourceTransactionObject
*/
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
//isExistingTransaction,默认返回false,同样被具体的事务管理器子类重写
//DataSourceTransactionManager的方法将会判断上面获取的DataSourceTransactionObject内部的数据库连接connectionHolder属性是否不为null,
// 并且是否已经开启了事务。我们说过如果当前线程是第一次进来,那么connectionHolder就是null。
if (isExistingTransaction(transaction)) {
//如果已经存在事务,那么将检查传播行为并进行不同的处理,随后返回
return handleExistingTransaction(def, transaction, debugEnabled);
}
/*
* 到这里表示没有已存在的事务,进入第一个事务方法时将会走这个逻辑
*/
//设置的事务超时时间如果小于默认超时时间(-1),将会抛出异常
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
//如果配置的事务的传播行为是PROPAGATION_MANDATORY,该传播行为的含义是:
//如果当前存在事务,则当前方法加入到该事务中去,如果当前不存在事务,则当前方法直接抛出异常。
//这里就直接抛出异常。
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
//否则,如果配置的事务的传播行为是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) {
//暂停给定的事务。首先挂起事务同步,然后再委派给doSuspend模板方法。
//由于此前没有事务,所以参数事务为null
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
//开启一个新事务并返回
return startTransaction(def, transaction, debugEnabled, suspendedResources);
} catch (RuntimeException | Error ex) {
//唤醒此前挂起的事务和资源
resume(null, suspendedResources);
throw ex;
}
}
//否则,配置的事务的传播行为就是剩下的三种:PROPAGATION_SUPPORTS或PROPAGATION_NEVER或PROPAGATION_NOT_SUPPORTED,
//这几个传播行为的含义的共同点之一就是:当前方法一定以非事务的方式运行。
else {
/*将会创建“空”事务:即没有实际的事务,但是有潜在的同步性。*/
//如果配置的事务的隔离级别不是是u巨酷默认的隔离级别,那么输出警告:
//虽然指定了自定义的隔离级别,但是由于未启动任何事物,那么隔离级别也就不会生效了
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);
//为给定参数创建一个新的TransactionStatus,并在适当时初始化事务同步,但是不会真正开启事务。
//和startTransaction相比,其内部会调用newTransactionStatus和prepareSynchronization,但不会调用doBegin方法
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
3.1 doGetTransaction获取事务连接
返回当前已存在的事务对象,返回的对象应包含有关任何现有事务的信息,即,在事务管理器上的当前getTransaction方法调用之前已经启动的事务。因此,doGetTransaction的实现通常是将查找现有事务并将相应的状态存储在返回的事务对象中。
返回的对象通常特定于具体的事务管理器子类自己实现。一般都使用DataSourceTransactionManager这个事务管理器,它的doGetTransaction方法逻辑如下:
- 创建一个
DataSourceTransactionObject
对象,由DataSourceTransactionManager用作内部事务对象。可能会持有一个ConnectionHolder对象,还具有创建、回滚、释放保存点的功能。 - 设置
是否允许保存点
,DataSourceTransactionManager默认会允许,一般用于实现嵌套事务。 obtainDataSource
方法用于获取配置的数据源,就是我们自己配置的数据源,getResource用于获取此线程在当前数据源中已拥有JDBC连接资源持有者。如果此前没有获取过连接,则返回null;如果此前开启了过事务(外层事务),那么肯定不会获取null。- 设置
连接信息
,newConnectionHolder属性设置为false,这表示默认此前已经存在ConnectionHolder,但实际上可能并没有,因此后面的步骤中会再次判断该值。 - 最后返回DataSourceTransactionObject。
/**
* DataSourceTransactionManager的方法
* <p>
* 返回当前事务状态的事务对象,返回的对象通常特定于具体的事务管理器实现。
* 返回的对象应包含有关任何现有事务的信息,即,在事务管理器上的当前getTransaction方法调用之前已经启动的事务。
* 因此,doGetTransaction的实现通常是将查找现有事务并将相应的状态存储在返回的事务对象中。
* <p>
* 对于DataSourceTransactionManager,将会返回DataSourceTransactionObject
*
* @return 一个DataSourceTransactionObject对象
*/
@Override
protected Object doGetTransaction() {
//创建一个DataSourceTransactionObject,由DataSourceTransactionManager用作内部事务对象。
//内部可能会持有一个ConnectionHolder对象,还具有创建、回滚、释放保存点的功能
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
//设置是否允许保存点,DataSourceTransactionManager默认会允许,用于实现嵌套事务
txObject.setSavepointAllowed(isNestedTransactionAllowed());
//obtainDataSource方法用于获取配置的数据源,就是我们自己配置的数据源
//getResource用于获取此线程在当前数据源中已拥有JDBC连接资源持有者ConnectionHolder
//如果此前没有获取过连接,则返回null;如果此前开启了过事务(外层事务),那么肯定不会获取null
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
//设置连接信息,newConnectionHolder属性设置为false,这表示默认此前已经存在ConnectionHolder
//但实际上可能并没有,因此后面的步骤中会再次判断该值
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
3.1.1 TransactionSynchronizationManager事务同步管理器
上面的源码中我们见到了一个TransactionSynchronizationManager类,这个类比较特别,虽然它的名字带有Transaction以及Manager,但却不是TransactionManager体系,它没有任何继承树,因此它可以看作一个很特别的工具类!
该类被称为事务同步管理器。主要用于管理每一个线程当前所使用的数据库事务连接资源和事务同步器(TransactionSynchronization)。
一个线程在当前只能激活一个连接资源,因此如果需要绑定新的连接资源,那么需要将此前绑定的资源删除(或者说保存起来,等新资源使用完毕之后再恢复此前的连接资源)。另外还能支持事务同步列表,事务同步必须由事务管理器通过initSynchronization()和clearSynchronization()来进行激活和停用,使用isSynchronizationActive()检测当前是否具有事务同步。
TransactionSynchronizationManager内部是通过很多ThreadLocal(线程本地变量)类型的属性来实现为当前线程维护自己的资源的功能的,实现资源隔离。
/**
* 事务资源管理器
*/
public abstract class TransactionSynchronizationManager {
/**
* 事务资源
* <p>
* 一个ThreadLocal属性,用于存放线程当前使用的数据库资源
* value是一个Map<Object, Object>,key为某个数据源DataSource ,value实际上就是连接ConnectionHolder
*/
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
/**
* 事务同步
* <p>
* 一个ThreadLocal属性,用于存放线程当前激活的事务同步器TransactionSynchronization
* 每个线程都可以开启多个事物同步,用于在处理事务的各个阶段进行自定义扩展或者回调
* <p>
* TransactionSynchronization的同步回调功能类似于此前学习的@TransactionalEventListener
*/
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
/**
* 当前事务的名称
* <p>
* 一个ThreadLocal属性,用于存放线程当前的事务的名称
*/
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
/**
* 当前事务的只读状态
* <p>
* 一个ThreadLocal属性,用于存放线程当前的事务的只读状态
*/
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");
/**
* 当前事务的隔离级别
* <p>
* 一个ThreadLocal属性,用于存放线程当前的当前事务的隔离级别
*/
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");
/**
* 当前事务是否开启
* <p>
* 一个ThreadLocal属性,用于存放线程当前是否开启了事务
*/
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");
//与事务资源相关的管理方法
//………………
}
3.1.2 TransactionSynchronization事务同步
TransactionSynchronization是一个事务同步回调的接口。简单的说,该类可以记录事务的执行状态,并且可以在某个状态转变时执行各种回调操作,功能类似于此前学过的@TransactionalEventListener监听器,但是该接口需要我们手动实现,并且支持Order排序。
/**
* 事务同步接口,记录是无状态并执行事务状态回调
*/
public interface TransactionSynchronization extends Flushable {
/**
* 正确提交的情况下的完成状态。
*/
int STATUS_COMMITTED = 0;
/**
* 正确回滚的情况下的完成状态。
*/
int STATUS_ROLLED_BACK = 1;
/**
* 启发式混合完成或系统错误时的完成状态。
*/
int STATUS_UNKNOWN = 2;
/**
* 暂停此同步。
* <p>
* 如果管理任何资源,则应取消与TransactionSynchronizationManager的资源绑定。
*/
default void suspend() {
}
/**
* 暂停此同步。
* <p>
* 如果管理任何资源,则应取消与TransactionSynchronizationManager的资源绑定。
*/
default void resume() {
}
/**
* 将基础会话刷新到数据存储(例如,Hibernate / JPA session可以使用)
*/
@Override
default void flush() {
}
/**
* 在事务提交之前调用(在"beforeCompletion"方法之前调用),此时可以将事务性O/R映射会话刷新到数据库。
* <p>
* 此回调并不意味着将实际提交事务。在调用此方法后,仍可能发生事务回滚。
* 相反,此回调旨在执行仅在提交仍有机会发生时才有意义的工作,例如将SQL语句刷新到数据库中。
* 请注意,这里抛出的异常将传播到提交调用者,并导致事务回滚。
*
* @param readOnly 事务是否定义为只读事务
*/
default void beforeCommit(boolean readOnly) {
}
/**
* 在事务提交/回滚之前调用。可以在事务完成之前执行资源清理。
* <p>
* 即使在beforeCommit引发异常时,也会在beforeCommit之后调用此方法。
* 对于任何结果,此回调都允许在事务完成之前关闭资源。
*/
default void beforeCompletion() {
}
/**
* 事务提交后调用。成功完成主要事务后,可以立即执行进一步的操作。
* 该方法最常用。比如完成数据成功入库之后发送一条确认短信、一封邮件……
* <p>
* 事务将已经提交,但是事务资源可能仍处于活动状态并且可以访问。
* 结果就是,此时触发的任何数据访问代码仍将"参与"原始事务,
* 从而允许执行一些清除操作(不再进行任何提交!),除非它明确声明需要在单独的事务中运行。
* <p>
* 如果需要从此处调用的任何事务操作,那么需要使用PROPAGATION_REQUIRES_NEW。
*/
default void afterCommit() {
}
/**
* 在事务提交/回滚后调用。事务完成后可以执行资源清理。
* <p>
* 事务将已经提交,但是事务资源可能仍处于活动状态并且可以访问。
* 结果就是,此时触发的任何数据访问代码仍将"参与"原始事务,
* 从而允许执行一些清除操作(不再进行任何提交!),除非它明确声明需要在单独的事务中运行。
* <p>
* 如果需要从此处调用的任何事务操作,那么需要使用PROPAGATION_REQUIRES_NEW。
*
* @param status 完成状态
*/
default void afterCompletion(int status) {
}
}
3.1.3 obtainDataSource获取数据源
获取当前DataSourceTransactionManager实际使用的数据源,就是我们配置给事务管理器的DataSource。
/**
* DataSourceTransactionManager的方法
* <p>
* 获取实际使用的数据源。
*
* @return 数据源(绝不为null)
*/
protected DataSource obtainDataSource() {
DataSource dataSource = getDataSource();
Assert.state(dataSource != null, "No DataSource set");
return dataSource;
}
/**
* DataSourceTransactionManager的属性
* <p>
* 就是我们配置的数据源
*/
@Nullable
private DataSource dataSource;
/**
* DataSourceTransactionManager的方法
* <p>
* 返回此事务管理器内部的JDBC DataSource。
*/
@Nullable
public DataSource getDataSource() {
return this.dataSource;
}
3.1.4 getResource获取已存在的连接
TransactionSynchronizationManager的方法,该方法检索给定key绑定到当前线程的资源,实际上就是尝试从resources属性中获绑定当前线程的从给定数据源获已取到的连接资源ConnectionHolder。
如果此前没有获取过此数据源的连接,那么将会得到一个null值,即如果是第一次进入事务方法,那么将返回null。
这里resources属性的value是一个Map<Object, Object>,这说明一个线程可以从不同的数据源中获取资源,但是对于同一个数据源,只能保存一个的数据源。
/**
* TransactionSynchronizationManager的方法
* <p>
* 检索绑定到当前线程的给定key的资源。
*
* @param key 通常是资源工厂,比如说我们配置的某个DataSource数据源对象
* @return 绑定到当前线程的给定key的资源value,如果没有则为null
*/
@Nullable
public static Object getResource(Object key) {
//如有必要,解开给定的资源句柄,否则按原样返回给定的句柄,用常用于从各种代理对象中获取原始对象
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
/*真正获取当前绑定的资源*/
Object value = doGetResource(actualKey);
if (value != null && logger.isTraceEnabled()) {
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
Thread.currentThread().getName() + "]");
}
return value;
}
/**
* 事务资源
*/
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
/**
* TransactionSynchronizationManager的方法
* <p>
* 实际检查给定key绑定的资源的value值。
*
* @return 给定key绑定的资源的value值,如果没找到就返回null
*/
@Nullable
private static Object doGetResource(Object actualKey) {
//获取和当前线程绑定的数据库事务资源map
Map<Object, Object> map = resources.get();
//如果map为null,说明此前没有开启过事务,直接返回null
if (map == null) {
return null;
}
//对于DataSourceTransactionManager,就是
//根据指定key(数据源DataSource)获取对应的连接ConnectionHolder
Object value = map.get(actualKey);
//删除无效的ResourceHolder
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
// Remove entire ThreadLocal if empty...
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
3.2 isExistingTransaction是否已存在事务
检查是否已经开启过事务,默认返回false,被具体的事务管理器子类重写。
DataSourceTransactionManager的逻辑很简单,判断通过doGetTransaction方法获取的DataSourceTransactionObject内部的数据库连接connectionHolder属性是否不为null,并且是否已经开启了事务。
我们说过如果当前线程是第一次进来,那么connectionHolder就是null。
/**
* DataSourceTransactionManager的方法
* <p>
* 检查是否有现有事务(即已经开启过事务)。
*
* @param transaction 此前获取的事务对象
* @return 如果已存在事务则返回true,否则返回false
*/
@Override
protected boolean isExistingTransaction(Object transaction) {
//强转为DataSourceTransactionObject
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
//判断内部的数据库连接connectionHolder是否不为null并且已经开启了事务
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
3.3 suspend挂起事务
挂起给定的事务。首先挂起当前线程的事务同步回调,然后再委派给doSuspend
模板方法由子类来实现挂起当前事务,并且还会清空TransactionSynchronizationManager
中保存的当前线程的事务信息。
最终将会返回会被挂起的资源只有者SuspendedResourcesHolder
。如果没有事务同步也没有事务,那么将会返回null。当前线程第一次进入事务方法时,默认将会返回null。
/**
* AbstractPlatformTransactionManager的方法
* <p>
* 挂起给定的事务。首先挂起当前线程的事务同步回调,然后再委派给doSuspend模板方法由子类来实现挂起当前事务。
*
* @param transaction 当前事务对象(如果为null,则仅挂起活动同步)
* @return 一个拥有挂起的资源的对象(如果没有事务或同步未激活,则为null)
*/
@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
//如果当前线程的事务同步处于活动状态,即存在绑定的TransactionSynchronization,则返回true。
//如果是第一次因为进来,那么自然为false
if (TransactionSynchronizationManager.isSynchronizationActive()) {
//挂起当前线程的所有事务同步回调,这类似于@TransactionalEventListener,并返回"被挂起"的回调
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
//挂起事务,由具体的子类实现
suspendedResources = doSuspend(transaction);
}
/*获取当前事务的信息,并且清空各个ThreadLocal缓存中的当前线程的当前事务信息(恢复为默认值)*/
//获取并清空(设置为null)事物名称
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
//获取并清空(设置为false)事物只读状态
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
//获取并清空(设置为null)事物隔离级别
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
//获取并清空(设置为false)事物是否激活
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
//将获取的当前事物的信息存入一个SuspendedResourcesHolder对象中返回
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
} catch (RuntimeException | Error ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
//如果没有事务同步但是开启了事务,那么挂起事务
else if (transaction != null) {
//挂起事务,由具体的子类实现
Object suspendedResources = doSuspend(transaction);
//将挂起的资源存入一个SuspendedResourcesHolder对象中返回
return new SuspendedResourcesHolder(suspendedResources);
} else {
//事务或者事务同步均未激活,返回null,什么也不干
return null;
}
}
3.3.1 isSynchronizationActive是否激活事务同步
TransactionSynchronizationManager的方法,用来判断线程在当前是否已激活事务同步TransactionSynchronization。
实际上就是synchronizations
属性中是否有绑定到当前线程的Set<TransactionSynchronization>
集合,如果有(不为null),那就说明存在事务同步。
/**
* TransactionSynchronizationManager的属性
* <p>
* 一个ThreadLocal类型的属性,每个线程都可以开启事物同步,用于在处理事务的各个阶段进行自定义扩展或者回调
* TransactionSynchronization的同步回调功能类似于此前学习的@TransactionalEventListener
*/
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
/**
* TransactionSynchronizationManager的方法
* <p>
* 判断当前线程的是否注册了事务同步
*/
public static boolean isSynchronizationActive() {
//获取当前线程绑定的TransactionSynchronization,如果不为null就说明注册了事务同步
return (synchronizations.get() != null);
}
3.3.2 doSuspendSynchronization挂起事务同步
该方法挂起当前线程在TransactionSynchronizationManager的synchronize并且将属性中为当前线程保持的事务同步列表引用移除,最后返回被挂起的事务同步列表!
/**
* AbstractPlatformTransactionManager的方法
* <p>
* 挂起所有当前同步,并停用当前线程的事务同步。
*
* @return 挂起的TransactionSynchronization对象的列表
*/
private List<TransactionSynchronization> doSuspendSynchronization() {
//获取线程的当前的所有事务同步列表
List<TransactionSynchronization> suspendedSynchronizations =
TransactionSynchronizationManager.getSynchronizations();
//遍历,依次挂起每一个事务同步
for (TransactionSynchronization synchronization : suspendedSynchronizations) {
synchronization.suspend();
}
//清除synchronizations属性中保存的的当前线程的当前事务同步集合的引用
TransactionSynchronizationManager.clearSynchronization();
//返回被挂起的事务同步
return suspendedSynchronizations;
}
/**
* TransactionSynchronizationManager的方法
* 调用该方法时一定要保证当前线程存在事务同步,否则将抛出异常
* 因此需要先调用isSynchronizationActive方法来校验
* <p>
* 返回当前线程的所有已注册的事务同步的无法修改的快照列表
*
* @return 无法修改的TransactionSynchronization实例列表
* @throws IllegalStateException 如果同步未激活
*/
public static List<TransactionSynchronization> getSynchronizations() throws IllegalStateException {
//获取当前线程的事务同步列表
Set<TransactionSynchronization> synchs = synchronizations.get();
//为null就抛出IllegalStateException异常
if (synchs == null) {
throw new IllegalStateException("Transaction synchronization is not active");
}
// 返回不可修改的快照,以避免在迭代和调用可能进一步注册同步的同步回调时抛出ConcurrentModificationExceptions。
if (synchs.isEmpty()) {
return Collections.emptyList();
} else {
//在获取的之后对快照进行排序
List<TransactionSynchronization> sortedSynchs = new ArrayList<>(synchs);
AnnotationAwareOrderComparator.sort(sortedSynchs);
return Collections.unmodifiableList(sortedSynchs);
}
}
/**
* TransactionSynchronizationManager的方法
* 调用该方法时一定要保证当前线程存在事务同步,否则将抛出异常
* 因此需要先调用isSynchronizationActive方法来校验
* <p>
* 停用当前线程的事务同步,由事务管理器在事务清理中调用。
*
* @throws IllegalStateException 如果同步未激活
*/
public static void clearSynchronization() throws IllegalStateException {
//如果没有激活事务同步,同样抛出异常
if (!isSynchronizationActive()) {
throw new IllegalStateException("Cannot deactivate transaction synchronization - not active");
}
logger.trace("Clearing transaction synchronization");
//移除当前线程绑定到synchronizations属性的值
synchronizations.remove();
}
3.3.3 doSuspend挂起事务
其核心就是doSuspend方法,该方法默认抛出异常,由子类自己实现!
DataSourceTransactionManager重写的方法很简单,就是将DataSourceTransactionObject中的connectionHolder设置为null,并且将给定数据源绑定到当前线程的连接资源从TransactionSynchronizationManager的resources属性中移除并返回,这就是DataSourceTransactionManager被挂起的连接资源,就是此前获取的连接。
从这里能够看出,所谓的“挂起”,就是将当前的连接从绑定的线程本地变量中移除!
/**
* DataSourceTransactionManager的方法
* <p>
* 挂起当前事务,返回当前的连接资源
*
* @param transaction 挂起事务
* @return 被挂起的资源
*/
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
//将当前的事务对象的connectionHolder设置为null
txObject.setConnectionHolder(null);
//将当前线程的绑定的当前数据源对应的连接同样移除,并且返回被移除的连接资源
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
/**
* TransactionSynchronizationManager的方法
* <p>
* 移除当前线程中给定key绑定的资源的值
*
* @param key 就是当前数据源
* @return 被移除的资源,就是连接
*/
public static Object unbindResource(Object key) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
//真正的移除指定的key对应的连接
Object value = doUnbindResource(actualKey);
if (value == null) {
throw new IllegalStateException(
"No value for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
}
return value;
}
/**
* 事务资源
*/
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
/**
* TransactionSynchronizationManager的方法
* <p>
* 移除当前线程中给定key绑定的资源的值。
*
* @param actualKey 就是当前数据源
* @return 被移除的资源,就是连接
*/
@Nullable
private static Object doUnbindResource(Object actualKey) {
//获取和当前线程绑定的数据库资源map
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
//从map中移除从当前数据源对应的连接缓存
Object value = map.remove(actualKey);
//如果map为空,则删除整个ThreadLocal。
if (map.isEmpty()) {
resources.remove();
}
// Transparently suppress a ResourceHolder that was marked as void...
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
value = null;
}
if (value != null && logger.isTraceEnabled()) {
logger.trace("Removed value [" + value + "] for key [" + actualKey + "] from thread [" +
Thread.currentThread().getName() + "]");
}
//返回被移除的资源
return value;
}
3.4 startTransaction开启新事物
在挂起此前的事务同步或者事务之后,将会通过startTransaction方法开启新事务,返回一个TransactionStatus的实现,默认类型为DefaultTransactionStatus,这个TransactionStatus就代表了当前开启的事务对象,其内部还保存了此前通过suspend方法挂起的事务资源(suspendedResources属性),用于后续恢复之前的事务。
/**
* AbstractPlatformTransactionManager的方法
* <p>
* 开启一个新事物
*
* @param definition 为当前方法配置的事务定义
* @param transaction 获取的事务对象,对于DataSourceTransactionManager来说就是DataSourceTransactionObject
* @param debugEnabled 日志级别
* @param suspendedResources 被挂起的资源
* @return 新开启的事务TransactionStatus
*/
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
//判断是否需要开启新同步,默认都是SYNCHRONIZATION_ALWAYS,即需要开启
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
//新建一个TransactionStatus对象,内部持有transaction对象
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
/*
* 真正的开启事务,该方法由具体的子类自己实现
*/
doBegin(transaction, definition);
//准备事务同步
prepareSynchronization(status, definition);
return status;
}
3.4.1 newTransactionStatus开启新事物
新建一个DefaultTransactionStatus实现并返回,内部持有我们为当前方法配置的事务属性或者默认属性,以及保存着此前挂起的其他资源。
/**
* AbstractPlatformTransactionManager的方法
* <p>
* 为给定参数新创建一个TransactionStatus实例,实际类型为DefaultTransactionStatus
*
* @param definition 为当前方法配置的事务定义
* @param transaction 获取的事务对象
* @param newTransaction 是否是新事物
* @param newSynchronization 是否开启事务同步
* @param debug 是否支持debug级别的日志
* @param suspendedResources 被挂起的资源,比如此前的事务同步
* @return DefaultTransactionStatus对象
*/
protected DefaultTransactionStatus newTransactionStatus(
TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {
//如果newSynchronization为true并且当前线程没有绑定的事务同步,那么确定开启新事物同步
//由于此前调用了suspend方法清理了此前的事务同步,因此一般都是需要开启新事务同步,即为true
boolean actualNewSynchronization = newSynchronization &&
!TransactionSynchronizationManager.isSynchronizationActive();
//返回一个新建的DefaultTransactionStatus对象,该对象被用来表示新开启的事务,是TransactionStatus的默认实现
//内部包括了各种新开启的事务状态,当然包括此前挂起的事务的资源
return new DefaultTransactionStatus(
transaction, newTransaction, actualNewSynchronization,
definition.isReadOnly(), debug, suspendedResources);
}
/*DefaultTransactionStatus的属性*/
/**
* 从事务管理器获取的内部事务
* 对于DataSourceTransactionManager来说就是DataSourceTransactionObject
*/
@Nullable
private final Object transaction;
/**
* 是否是新事物
*/
private final boolean newTransaction;
/**
* 是否开启新事务同步
*/
private final boolean newSynchronization;
/**
* 是否开启新事务同步
*/
private final boolean readOnly;
/**
* 是否支持debug日志级别
*/
private final boolean debug;
/**
* 此前被挂起的事务资源
*/
@Nullable
private final Object suspendedResources;
public DefaultTransactionStatus(
@Nullable Object transaction, boolean newTransaction, boolean newSynchronization,
boolean readOnly, boolean debug, @Nullable Object suspendedResources) {
this.transaction = transaction;
this.newTransaction = newTransaction;
this.newSynchronization = newSynchronization;
this.readOnly = readOnly;
this.debug = debug;
this.suspendedResources = suspendedResources;
}
3.4.2 doBegin真正开启事务
该方法是核心方法,当事务管理器决定实际开始新事务时,将调用此方法。此时之前可能没有任何事务,或者先前的事务已被暂停。
根据给定的事务定义TransactionDefinition,以及此前通过doGetTransaction方法返回的事务对象(也就是DataSourceTransactionObject),使用给定的语义开始一个新事务。不必关心应用传播行为,因为抽象事务管理器已经处理了该行为。
大概步骤为:
- 从数据源中获取一个新连接
Connection
,并且包装为一个ConnectionHolder
对象。ConnectionHolder设置为DataSourceTransactionObject事务对象的connectionHolder属性,并且将newConnectionHolder属性设置为true。 - 初始化连接的各种属性,比如
隔离级别、只读标志
等。 - 通过
con.setAutoCommit(false)
,设置当前连接为手动提交,真正开启事物
! - 设置ConnectionHolder的
transactionActive属性为true
,表示激活当前连接的事务。 - 设置
超时时间
。如果不是默认超时时间-1,那么将根据设置的值和当前时间转换为未来的毫秒值并创建新Date配置给ConnectionHolder的deadline属性,在其他数据库操作框架操作时将会获取该参数。 - 如果是新连接,即newConnectionHolder属性为true,
绑定ConnectionHolder资源到TransactionSynchronizationManager的resources属性中,就是前面说的事务资源,key就是当前的属性源dataSource,value就是ConnectionHolder。
/**
* DataSourceTransactionManager的方法
* <p>
* 开启新事物
* 并不会处理事务的传播行为,因为传播行为是Spring提供的特性,在事务管理器中就被直接处理了
*
* @param transaction 此前的doGetTransaction方法返回的事务对象,也就是DataSourceTransactionObject
* @param definition 一个TransactionDefinition实例,描述传播行为,隔离级别,只读标志,超时时间和事务名称等属性
*/
@Override
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();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
/*
* 新建一个ConnectionHolder对象,其内部保存这获取的连接,
* 将使用SimpleConnectionHandle包装获取的连接并且设置为connectionHandle属性
*
* 该ConnectionHolder被设置给txObject的connectionHolder属性
* 以及将newConnectionHolder属性设置为true,表示是一个新连接
*
* 到这里,事务对象就已经获得了一个新连接
*/
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//获取事务对象的连接持有者,将synchronizedWithTransaction设置为true,即资源标记为与事务同步。
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
//获取内部保存的连接
con = txObject.getConnectionHolder().getConnection();
/*
* 使用给定的事务语义准备给定的Connection,就是设置数据库事务的隔离级别,只读标志属性
*
* 如果我们配置的隔离级别属性是ISOLATION_DEFAULT,即采用默认隔离级别,或者不是默认的隔离级别但是与连接的隔离级别一致,那么将返回null
* 否则将返回从连接中直接获取的隔离级别(如果有)
*/
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
//此前的隔离级别设置给事务对象的previousIsolationLevel属性
txObject.setPreviousIsolationLevel(previousIsolationLevel);
//只读标志设置给事务对象的readOnly属性
txObject.setReadOnly(definition.isReadOnly());
//如有必要,切换为手动提交。
//从Druid数据源中获取的连接DruidPooledConnection就是默认自动提交,即getAutoCommit返回true
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
/*
* 如果上面判断是自动提交,那么切换为手动提交,为什么呢?如果不手动提交,
* 那么一个方法中执行多个sql语句时将会每执行一个提交一次,无法实现事务的控制
* 开启手动提交就能实现方法级别的整体事务控制
*
* 并且,开启手动提交时,将会自动开启事物
*/
con.setAutoCommit(false);
}
//事务已经开启,此后的sql语句,如果没有手动commit,那么将不会真正的提交给数据库
//用户本次对数据库开始进行操作到用户执行commit命令之间的一系列操作为一个完整的事务周期。
/*
*
* 事务开始后立即准备事务连接,主要是对于只读事务的优化操作(需要手动开启)
* 如果将"enforceReadOnly"标志设置为true(默认为false),并且事务定义指示只读事务,
* 则默认实现将执行"SET TRANSACTION READ ONLY"这一个sql语句。
* 请注意mysql只读事务不要开启,oracle的只读事务可以开启
*/
prepareTransactionalConnection(con, definition);
//设置事务ConnectionHolder的transactionActive属性为true,表示激活当前连接的事务
//此前判断是否有开启事务的isExistingTransaction方法就会判断这个属性
txObject.getConnectionHolder().setTransactionActive(true);
/*
* 设置实际超时时间
*/
int timeout = determineTimeout(definition);
//如果不是默认超时时间-1,那么将
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
//那么设置超时时间,实际上就是根据设置的值和当前时间转换为未来的毫秒值并创建新Date配置给deadline属性
//在其他数据库操作框架操作时将会获取该参数
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
//如果是新的连接持有者,即newConnectionHolder属性为true
if (txObject.isNewConnectionHolder()) {
//绑定ConnectionHolder资源到TransactionSynchronizationManager的resources属性中
//key就是当前的属性源,value就是ConnectionHolder
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);
}
}
3.4.2.1 prepareConnectionForTransaction准备事务连接
使用给定的事务语义准备给定的Connection,就是将我们设置的隔离级别isolationLevel,只读标志readOnly属性赋给当前事务连接。
如果我们配置的隔离级别属性是ISOLATION_DEFAULT,即采用默认隔离级别,或者不是默认的隔离级别但是与连接的隔离级别一致,那么将返回null,否则将设置连接的隔离级别为指定的级别,并且返回从连接中获取的隔离级别(如果有)。
/**
* DataSourceUtils的方法
* <p>
* 使用给定的事务语义准备给定的Connection。
*
* @param con 需要准备的连接
* @param definition 适用的事务定义
* @return 连接先前的隔离级别,可能为null
*/
@Nullable
public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)
throws SQLException {
Assert.notNull(con, "No Connection specified");
boolean debugEnabled = logger.isDebugEnabled();
// 设置只读标志。
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);
}
}
// 应用特定的隔离级别(如果有)。
Integer previousIsolationLevel = null;
//如果存在隔离级别并且不等于默认配置,即不等于ISOLATION_DEFAULT(该级别的意思是使用数据库的默认级别)
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());
}
}
//返回此前的连接的隔离级别,可能为null
return previousIsolationLevel;
}
4.4.2.2 prepareTransactionalConnection优化只读事务
事务开始后立即准备事务连接,主要是对于只读事务的优化操作(需要手动开启)。如果将事务管理器的"enforceReadOnly"标志设置为true(默认为false)
,并且事务定义指示只读事务,则默认实现将执行"SET TRANSACTION READ ONLY"
这一个sql语句。
"SET TRANSACTION READ ONLY"
这个sql的意思就是告诉数据库,此事务中的后续sql语句将只有查询操作,不能进行DML操作。在"SET TRANSACTION READ ONLY"之后的查询语句将不会查询到该事物期间提交的内容,只能查询到事务开始之前提交的内容,相当于查询一个快照。
进行只读事务设置之后,将有效减轻数据库压力。对于同一个表进行更新操作时,只读事务不会被阻塞,可以正常的执行查询操作,在只读事务操作期间也不会影响其他事务!
/**
* DataSourceTransactionManager的方法
* 如果将"enforceReadOnly"标志设置为true,并且事务定义指示只读事务,
* 则默认实现将执行"SET TRANSACTION READ ONLY"sql语句。
*
* @param con 当前连接
* @param definition 为当前方法配置的事务定义
*/
protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)
throws SQLException {
//如果将"enforceReadOnly"标志设置为true,并且事务定义指示只读事务
if (isEnforceReadOnly() && definition.isReadOnly()) {
//那么获取Statement,并且执行"SET TRANSACTION READ ONLY"sql语句
try (Statement stmt = con.createStatement()) {
stmt.executeUpdate("SET TRANSACTION READ ONLY");
}
}
}
/**
* DataSourceTransactionManager的属性
* <p>
* 表示是否通过对事务连接显式执行sql语句强制执行事务的只读性质,默认为false
*/
private boolean enforceReadOnly = false;
/**
* 返回是否通过对事务连接显式执行sql语句强制执行事务的只读性质。
*/
public boolean isEnforceReadOnly() {
return this.enforceReadOnly;
}
上面说了这么多好处,很遗憾的是DataSourceTransactionManager的enforceReadOnly属性默认为false,并且大部分开发者也不知道这个优化,因此大多数情况下并不会执行该sql语句,即不会进行优化。
如果要开启,那么可以这么设置:
/**
* 配置DataSourceTransactionManager
* 用于管理某一个数据库的事务
*/
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(druidDataSource());
//设置只读事务优化
dataSourceTransactionManager.setEnforceReadOnly(true);
//传入一个数据源
return dataSourceTransactionManager;
}
当然如果你真的这么开启了,并且你通过@Transactional注解设置了某个方法的事务的readOnly属性为true,那么确实会执行该方法,但是你讲将会收到一个异常:“Connection is read-only. Queries leading to data modification are not allowed”。
原因是什么呢?很简单,在前面的prepareConnectionForTransaction方法中,连接被设置为只读,然而在随后的prepareTransactionalConnection方法中,执行该sql语句的却是executeUpdate方法,自然会抛出异常!
所以说,这个优化还不能随便开。或者说,是因为不同的数据库对于Spring的readOnly属性的支持是不一样的,mysql支持Spring的readOnly参数,即支持JDBC的con.setReadOnly(true),因此就没必要再设置enforceReadOnly为true,而oracle则仅支持在Oracle server的设置而非JDBC驱动的配置,因此不支持con.setReadOnly(true),所以实际上Spring的readOnly配置对于Oracle无效,所以Oracle数据库可以开启此优化,mysql则不必要。
4.4.2.3 determineTimeout确定超时时间
确定给定事务定义的实际超时时间。如果事务定义未指定非默认值,则将使用默认超时。
/**
* AbstractPlatformTransactionManager的方法
* <p>
* 确定给定事务定义的实际超时时间。
* 如果事务定义未指定非默认值,则将使用默认超时。
*
* @param definition 事务定义
* @return 实际使用的超时时间
*/
protected int determineTimeout(TransactionDefinition definition) {
//如果不是默认超时时间,那么使用指定的事件
if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
return definition.getTimeout();
}
//否则使用默认超时
return getDefaultTimeout();
}
/**
* AbstractPlatformTransactionManager的属性
* <p>
* 默认超时时间(秒),默认值为-1,表示使用基础事务系统的默认超时;
*/
private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;
public final int getDefaultTimeout() {
return this.defaultTimeout;
}
4.4.2.4 setTimeoutInSeconds设置超时deadline
这里实际上就是根据设置的值和当前时间转换为未来的毫秒值并创建新Date配置给deadline属性,在其他数据库操作框架具体操作时将会获取并应用该参数。
比如mybatis,在执行sql之前会获取到超时时间,计算之后会通过Statement.setQueryTimeout方法来设置,也就是说这个超时时间是执行sql之前的代码执行时间+sql执行时间,如果执行时间超过了设置时间就会抛出异常,这个异常就会被spring事务切面捕获到最终导致事务回滚,而如果在sql执行完毕之后的方法处理时间超过了这个超时时间,那么是不会进行回滚的,事务将会正常提交。
/**
* ConnectionHolder的父类ResourceHolderSupport的方法
* <p>
* 设置此对象的超时(以秒为单位)。
*
* @param seconds 到期前的秒数
*/
public void setTimeoutInSeconds(int seconds) {
setTimeoutInMillis(seconds * 1000L);
}
/**
* ConnectionHolder的父类ResourceHolderSupport的属性
*/
@Nullable
private Date deadline;
/**
* 设置此对象的超时(以毫秒为单位)。
*
* @param millis 到期前的毫秒数
*/
public void setTimeoutInMillis(long millis) {
//根据当前时间和超时时间计算出到期的Date
this.deadline = new Date(System.currentTimeMillis() + millis);
}
4.4.2.5 bindResource绑定资源到resources
对于新获取的连接资源会被绑定到TransactionSynchronizationManager的resources线程本地变量属性中(resources我们在此前就见过了)。key就是当前的属性源DataSource,value就是ConnectionHolder。
/**
* 事务资源
*/
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
/**
* TransactionSynchronizationManager的方法
* <p>
* 将给定key的给定资源value绑定到当前线程。
* 对于DataSourceTransactionManager,key就是DataSource实例,value就是ConnectionHolder
*
* @param key 将值绑定到的键(通常是资源工厂,比如dataSource)
* @param value 要绑定的值(通常是活动资源对象,比如数据库连接)
* @throws IllegalStateException 如果已经有绑定到线程的值
*/
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
//获取当前线程的本地资源map
Map<Object, Object> map = resources.get();
//如果找不到,则设置一个Map
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
//将actualKey和value存入map中,返回旧的value
Object oldValue = map.put(actualKey, value);
// Transparently suppress a ResourceHolder that was marked as void...
if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
//如果已经有绑定到线程的当前key的值,则抛出异常
if (oldValue != null) {
throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
}
if (logger.isTraceEnabled()) {
logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +
Thread.currentThread().getName() + "]");
}
}
4.2.4.3 prepareSynchronization准备事务同步
该方法通常用在doBegin开启新事物之后,用于准备事务同步,就是将当前事务的一系列属性绑定到TransactionSynchronizationManager的对应的线程本地变量中。
/**
* AbstractPlatformTransactionManager的方法
* <p>
* 适当地初始化事务同步。
*/
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
//是否是新同步,在真正的开启新事务的时候(比如第一次进入事务方法或者传播行为是REQUIRES_NEW),一般都是true
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();
}
}
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
/**
* TransactionSynchronizationManager的方法
* <p>
* 激活当前线程的事务同步。由事务管理器在事务开始时调用。
*
* @throws IllegalStateException 如果同步已处于活动状态
*/
public static void initSynchronization() throws IllegalStateException {
//如果同步已处于活动状态,即synchronizations保存的线程本地变量不为null,则抛出异常
if (isSynchronizationActive()) {
throw new IllegalStateException("Cannot activate transaction synchronization - already active");
}
logger.trace("Initializing transaction synchronization");
//否则就为当前初始化一个线程本地变量,这是一个空的LinkedHashSet
//虽然没有任何的TransactionSynchronization,但是已经不为null了
synchronizations.set(new LinkedHashSet<>());
}
3.2.5 prepareTransactionStatus准备事务状态
prepareTransactionStatus方法和上面的startTransaction方法相比,其内部会调用newTransactionStatus和prepareSynchronization,但不会调用doBegin方法,因此不会真正的开启事物。返回的TransactionStatus,其内部保存了其他的资源,比如被挂起的事务信息。
如果是第一次进入事务方法,即当前方法是最外层事务方法,并且传播行为是PROPAGATION_SUPPORTS或PROPAGATION_NEVER或PROPAGATION_NOT_SUPPORTED,那么会调用该方法,它的newTransaction参数为false,suspendedResources参数为null。即这些传播行为都不会真正的开启数据库级别的事务(不会获取新的连接)。
如果是已存在外层事务,即当前方法是内层事务方法,并且传播行为是PROPAGATION_NOT_SUPPORTED或者PROPAGATION_NESTED或者PROPAGATION_SUPPORTS或者PROPAGATION_REQUIRED或者PROPAGATION_MANDATORY,那么也有可能调用这个方法,它的newTransaction参数为false,suspendedResources参数为被挂起的外层事务资源。即这些传播行为都不会真正的开启数据库级别的事务(不会获取新的连接,对于普通事物来说)。
/**
1. 根据给定参数创建一个新的TransactionStatus,并在适当时初始化事务同步,不会真正开启新事物。
2. 3. @param definition 为当前方法设置的事务定义
4. @param transaction 当前已存在的事务
5. @param newTransaction 是否是新事物,如果是外层事务方法,则为true,如果是内层方法则为false
6. @param newSynchronization 是否是新事务同步
7. @param debug 是否支持debug日志
8. @param suspendedResources 被挂起的资源
9. @return 一个TransactionStatus的实现
*/
protected final DefaultTransactionStatus prepareTransactionStatus(
TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {
//通过newTransactionStatus创建一个DefaultTransactionStatus
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
//中间缺少了doBegin真正开启事务的访法,所以仅仅是创建了一个简单的TransactionStatus
//包存了一些其他信息,比如被挂起的资源信息
//准备事务同步
prepareSynchronization(status, definition);
return status;
}
3.2.6 handleExistingTransaction处理已存在事务
如果进入事务切面之后,获取到了已存在的连接并且开启了事务(通过isExistingTransaction方法判断),那么将会执行handleExistingTransaction方法执行已存在事务时的逻辑,并返回一个TransactionStatus。
该方法同样将会根据此事务切面设置的事务传播行为走不同的执行流程,比如加入当前事务、新建事务、抛出异常等等逻辑。
大概逻辑是:
- 如果当前配置的传播行为是
PROPAGATION_NEVER
,该行为的特点是:当前方法一定以非事务的方式运行,并且如果当前存在事务,则直接抛出异常,所以这里由于存在外部事务,那么直接抛出异常:“Existing transaction found for transaction marked with propagation ‘never’”。 - 如果当前配置的传播行为是
PROPAGATION_NOT_SUPPORTED
,该行为的特点是:当前方法一定以非事务的方式运行,如果当前存在事务,则把当前事务挂起,直到当前方法执行完毕,才恢复外层事务。- 首先调用
suspend
方法挂起外层事务,返回被挂起的资源。由于存在外层事务,所以这里的参数就是获取的外层事务参数。 - 随后调用
prepareTransactionStatus
方法返回一个新的TransactionStatus,并在适当时初始化事务同步。同样,该方法和上面的startTransaction方法相比,其内部会调用newTransactionStatus和prepareSynchronization,但不会调用doBegin方法,因此不会真正的开启事物。这里它的newTransaction参数为false,transaction参数为null,suspendedResources参数为被挂起的外层事务资源。
- 首先调用
- 如果当前配置的传播行为是
PROPAGATION_REQUIRES_NEW
,该行为的特点是:当前方法开启一个新事物独立运行,从不参与外部的现有事务。则当内部事务开始执行时,外部事务(如果存在)将被挂起,内务事务结束时,外部事务将继续执行。- 首先调用
suspend
方法挂起外层事务,返回被挂起的资源。由于存在外层事务,所以这里的参数就是获取的外层事务参数。 - 随后调用
startTransaction
方法真正的开启一个数据库级别的事务(将会获取新的连接开启一个新事物,旧的连接和事务则在上面的suspend方法中被挂起保存)。
- 首先调用
- 如果当前配置的传播行为是
PROPAGATION_NESTED
,该行为的特点是: 如果当前存在事务,则创建一个新“事务”作为当前事务的嵌套事务来运行;如果当前没有事务,则等价于PROPAGATION_REQUIRED,即会新建一个事务运行。- 调用
isNestedTransactionAllowed
方法判断是否允许PROPAGATION_NESTED行为,默认不允许,但是DataSourceTransactionManager重写为允许。不允许就抛出异常:“Transaction manager does not allow nested transactions……”。 - 调用useSavepointForNestedTransaction方法判断是否对“嵌套事务”使用保存点Savepoint来实现:
- 如果允许,那么首先调用
prepareTransactionStatus
方法返回一个新的TransactionStatus,并在适当时初始化事务同步。这里它的newTransaction参数为false,transaction参数为外层事务,suspendedResources参数为null,newSynchronization参数为false。随后调用createAndHoldSavepoint创建保存点,将使用数据库的保存点的特性来实现“嵌套事务”,这是用语大部分普通事务。 - 否则将调用
startTransaction
方法通过在外层事务中嵌套的begin和commit/rollback调用来开启真正的嵌套事务,不过通常仅用于JTA:如果存在预先存在的JTA事务,则可以在此处激活Spring同步。
- 如果允许,那么首先调用
- 调用
- 剩下的传播行为就是
PROPAGATION_SUPPORTS或者PROPAGATION_REQUIRED或者PROPAGATION_MANDATORY
,如果是这些行为,那么它们的一个共同的特性就是:参与到当前事务中去,也是默认传播行为的逻辑。- 那么这里同样调用
prepareTransactionStatus
方法,这里它的newTransaction参数为false,transaction参数为外层事务,suspendedResources参数为null。
- 那么这里同样调用
从源码中我们能够看到,如果传播行为是PROPAGATION_NESTED
:
- 对于
普通事务(比如DataSourceTransactionManager处理的事务)
,它的“嵌套事务”是通过SavePoint保存点来实现的,实际上就是同一个事务。基于保存点的特性,此时,“内层事务”依赖“外层事物”:层事务操作失败时只是自身回到到保存点的位置,不会引起外层事务的回滚,而外层事务因失败而回滚时,内层事务所做的所有动作也会回滚。在提交时,在外层事务提交之后内层事务才能提交,仅需要提交外层事务即可。由于实际上只有一个物理事务,那么内层事务会继承外层外层事务的隔离级别和超时设置等属性。 - 对于
JTA事务(分布式事务)
,我们能看到是调用了startTransaction方法,因此将会在原本的事务中通过嵌套的begin和commit/rollback调用来开启的嵌套事务,这是真正的嵌套事务。
/**
* AbstractPlatformTransactionManager的方法
* <p>
* 根据现有事务创建一个TransactionStatus,处理事务的传播行为
*
* @param definition 当前事务定义
* @param transaction 事物对象,内部包含此前的事务信息
* @param debugEnabled 日志级别支持
* @return 事务状态对象
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
/*
* 1 如果当前配置的传播行为是PROPAGATION_NEVER,该行为的特点是:
* 当前方法一定以非事务的方式运行,并且如果当前存在事务,则直接抛出异常
* 所以这里直接抛出异常
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
/*
* 2 如果当前配置的传播行为是PROPAGATION_NOT_SUPPORTED,该行为的特点是:
* 当前方法一定以非事务的方式运行,如果当前存在事务,则把当前事务挂起,直到当前方法执行完毕,才恢复外层事务。
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
//那么这里挂起外层事务
Object suspendedResources = suspend(transaction);
//判断是否需要进行新同步,默认都是需要的
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
//为给定参数创建一个新的TransactionStatus,并在适当时初始化事务同步。
//这里的第二个参数事务属性为null,表示当前方法以非事务的方式执行
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
/*
* 3 如果当前配置的传播行为是PROPAGATION_REQUIRES_NEW,该行为的特点是:
* 当前方法开启一个新事物独立运行,从不参与外部的现有事务。则当内部事务开始执行时,
* 外部事务(如果存在)将被挂起,内务事务结束时,外部事务将继续执行。
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
//那么这里挂起外层事务,返回被挂起的资源
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
//开启一个新事务并返回
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
} catch (RuntimeException | Error beginEx) {
//恢复被挂起的事务
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
/*
* 4 如果当前配置的传播行为是PROPAGATION_NESTED,该行为的特点是:
* 如果当前存在事务,则创建一个新“事务”作为当前事务的嵌套事务来运行;
* 如果当前没有事务,则等价于PROPAGATION_REQUIRED,即会新建一个事务运行。
*
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//判断是否允许PROPAGATION_NESTED行为,默认不允许,但是DataSourceTransactionManager重写为为允许
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
//返回是否对嵌套事务使用保存点,默认true,JtaTransactionManager设置为false
//PROPAGATION_NESTED就是通过Savepoint保存点来实现的
if (useSavepointForNestedTransaction()) {
//并没有挂起当前事务,创建TransactionStatus,transaction参数就是当前事务
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
/*
* 通过TransactionStatus实现的SavepointManager API在现有的Spring管理的事务中创建保存点,通常使用JDBC 3.0保存点。
*/
status.createAndHoldSavepoint();
return status;
} else {
// 通过在事务中嵌套的begin和commit / rollback调用开启的嵌套事务。
// 通常仅用于JTA:如果存在预先存在的JTA事务,则可以在此处激活Spring同步。
return startTransaction(definition, transaction, debugEnabled, null);
}
}
//剩下的传播行为就是PROPAGATION_SUPPORTS或者PROPAGATION_REQUIRED或者PROPAGATION_MANDATORY。
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
//是否在参与现有事务之前进行验证,默认false
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");
}
}
}
//并没有挂起当前事务,而是直接参与到当前事务中去,transaction参数就是当前的事务
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
3.2.6.1 createAndHoldSavepoint创建保存点
该方法很简单,最终会调用当前的JDBC连接Connection的setSavepoint
方法创建一个保存点,并且被设置给当前TransactionStatus对象的savepoint
属性。
/**
* AbstractTransactionStatus的方法
* <p>
* 创建一个保存点并将其保存在事务中。
*
* @throws NestedTransactionNotSupportedException 如果基础事务不支持保存点
*/
public void createAndHoldSavepoint() throws TransactionException {
setSavepoint(getSavepointManager().createSavepoint());
}
/**
* AbstractTransactionStatus的属性
*/
@Nullable
private Object savepoint;
/**
* AbstractTransactionStatus的方法
* <p>
* 设置此事务的保存点,对PROPAGATION_NESTED有用。
*/
protected void setSavepoint(@Nullable Object savepoint) {
this.savepoint = savepoint;
}
getSavepointManager
获取获取保存点管理器,实际上创建的内部事务对象都是SavepointManager接口的实现
,具有获取保存点的方法!因此返回的实际上就是内部的事务对象,对于DataSourceTransactionManager来说创建的内部事务就是DataSourceTransactionObject。
/**
* DefaultTransactionStatus的属性
* <p>
* 获取基础事务对象的SavepointManager,实际上就是获取的内部事务对象
*/
@Nullable
private final Object transaction;
/**
* DefaultTransactionStatus的方法
* <p>
* 获取基础事务对象的SavepointManager,实际上就是获取的内部事务对象
*/
@Override
protected SavepointManager getSavepointManager() {
//获取内部事务,对于DataSourceTransactionManager来说创建的内部事务就是DataSourceTransactionObject
Object transaction = this.transaction;
if (!(transaction instanceof SavepointManager)) {
throw new NestedTransactionNotSupportedException(
"Transaction object [" + this.transaction + "] does not support savepoints");
}
return (SavepointManager) transaction;
}
createSavepoint用于从当前保存点管理器(事务对象)中创建一个保存点,实际上就是获取事务对象里面的ConnectionHolder,然后在获取ConnectionHolder里面的Connection,最后调用Connection.setSavepoint
方法创建并获取保存点。
/**
* DataSourceTransactionObject的父类JdbcTransactionObjectSupport的方法
* <p>
* 创建一个JDBC 3.0保存点并返回它。
*/
@Override
public Object createSavepoint() throws TransactionException {
//校验规则并获取,此前创建的ConnectionHolder,其内部保存了获取的连接Connection
ConnectionHolder conHolder = getConnectionHolderForSavepoint();
try {
//如果不允许保存点,那么抛出异常,默认允许
if (!conHolder.supportsSavepoints()) {
throw new NestedTransactionNotSupportedException(
"Cannot create a nested transaction because savepoints are not supported by your JDBC driver");
}
//如果被设置为仅回滚,那么抛出异常
if (conHolder.isRollbackOnly()) {
throw new CannotCreateTransactionException(
"Cannot create savepoint for transaction which is already marked as rollback-only");
}
return conHolder.createSavepoint();
} catch (SQLException ex) {
throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);
}
}
/**
* DataSourceTransactionObject的父类JdbcTransactionObjectSupport的方法
* <p>
* 为了创建一个JDBC 3.0保存而获取ConnectionHolder。
*/
protected ConnectionHolder getConnectionHolderForSavepoint() throws TransactionException {
//如果不允许保存点,那么抛出异常,默认允许
if (!isSavepointAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions");
}
//如果没有ConnectionHolder,那么抛出异常,默认允许
if (!hasConnectionHolder()) {
throw new TransactionUsageException(
"Cannot create nested transaction when not exposing a JDBC transaction");
}
//返回此前创建的ConnectionHolder,其内部保存了获取的连接
return getConnectionHolder();
}
//ConnectionHolder的方法的属性
/**
* 保存点名称的前缀。
*/
public static final String SAVEPOINT_NAME_PREFIX = "SAVEPOINT_";
/**
* 从此连接中获取的保存点的数量
*/
private int savepointCounter = 0;
/**
* ConnectionHolder的方法
* 为当前连接创建一个新的JDBC 3.0保存点,只用SAVEPOINT_+savepointCounter作为保存点的名称
*
* @return 新的保存点
* @throws SQLException if thrown by the JDBC driver
*/
public Savepoint createSavepoint() throws SQLException {
//获取数量自增1
this.savepointCounter++;
//获取内部的JDBC连接,并通过连接设置一个保存点,返回创建的Savepoint
return getConnection().setSavepoint(SAVEPOINT_NAME_PREFIX + this.savepointCounter);
}
4 小结
createTransactionIfNecessary
方法创建并返回一个TransactionInfo
对象,并且在此过程中,将会调用getTransaction
方法获取事务TransactionStatus,getTransaction方法就是Spring事务处理的核心方法之一,该方法根据配置的各种事务传播行为以及是否存在外层事务做出不同的处理,方法执行完毕将可能开启了新事物,也可能没有开启,甚至抛出异常。
TransactionInfo
内部保存了事务管理器transactionManager、事务属性transactionAttribute、全路径方法名joinpointIdentification。还保存了当前方法的事务transactionStatus,以及前一个方法的事务信息对象oldTransactionInfo。
TransactionStatus实际类型为DefaultTransactionStatus,它持有一个transaction内部事务对象、被挂起的事务资源以及一些事务的属性,这个内部事务对象由事务管理器的实现各自创建,不同的事务管理器将会创建不同的类型,因此使用Object来表示,对于DataSourceTransactionManager来说,它创建的事务对象就是DataSourceTransactionObject。
DataSourceTransactionObject内部持有一个ConnectionHolder对象以及一些事务的属性,ConnectionHolder对象内部持有一个为了配置数据库事务而获取的JDBC连接Connection,以及是否开启了事务的标志transactionActive,以及其他属性,比如保存点计数。
最终,我们为某个方法定义的事务属性,除了传播行为之外(Spring提供的特性并自行处理),都会反应到Connection的对应操作上,比如隔离级别、超时时间,是否只读等等,关键方法就是doBegin,该方法用于真正的开启数据库层面的事务。
本次我们用了很长的文章讲解了createTransactionIfNecessary方法的逻辑和源码,这是Spring 事务开启的核心处理方法(可能并未真正的开启事务),剩下的方法比如proceedWithInvocation、completeTransactionAfterThrowing、cleanupTransactionInfo、
commitTransactionAfterReturning就是在开启事物之后的处理方法,比如回滚、提交、恢复事务等等,这些方法我们在后面的文章中讲解。
相关文章:
https://spring.io/
Spring Framework 5.x 学习
Spring Framework 5.x 源码
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!