参考资料:
总结
Spring把数据源对应的数据库连接存放在ThreadLocal的Map中,key是数据源,value是数据库连接。这样做的好处是保证同一个线程中操作的数据库连接都是相同的,而且业务层使用事务时不需要感知和管理数据库连接。
经过下面分析,可以总结出Spring事务处理的过程:
- 先获取@Transactional配置的属性值,包括发生什么异常进行回滚、事务传播行为、事务的隔离级别、事务管理器等
- 获取事务管理器,如果@Transactional中没有配置,则从spring容器中获取
- 收集事务信息,在这一步,根据事务传播行为进行不同的处理,如果是requires_new,则挂起父事务,开启子事务,如果是required,则加入当前事务,如果当前事务没开启,则开启新事务等
- 执行目标方法
- 执行过程中,发生异常,判断该异常是否为@Transactional的rollbackFor配置的异常相同或者其子类,是的话回滚,否则提交事务。
- 清理事务环境,清除ThreadLocal相关变量,就好像没有开启过事务一样
- 没有发生异常,最后提交或者回滚事务。如果当前事务设置了回滚标志位,则回滚事务(回滚到保存点、整个事务回滚)。如果当前事务是新事务,则提交事务
事务的传播行为
| required(常用) | 如果当前线程已经在一个事务中,则加入事务;如果没有事务,则新建一个 |
|---|---|
| requires_new(常用) | 不管是否存在事务,都新建一个事务,挂起当前事务 |
| support | 如果当前线程有事务,则加入。否则不使用事务 |
| not_supported | 如果当前线程有事务,则挂起事务;不支持使用事务 |
| mandatory | 如果当前线程有事务,则加入。否则抛出异常 |
| never | 不支持事务,当前线程事务则抛出异常 |
| nested | 嵌套事务,类似required,但有区别。mysql采用保存点实现 |
主要流程
Spring事务的处理流程代码主要在TransactionAspectSupport#invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
// 1.获取@Transactional的属性值
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 2.获取事务管理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 3.收集事务信息,开启或加入事务,具体操作根据配置的事务传播特性
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 4.执行被拦截的方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 5.根据@Transactional配置的rollbackFor指定的异常,决定回滚还是提交事务。
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 6.清除ThreadLocal设置的变量,好像没开启过事务一样
cleanupTransactionInfo(txInfo);
}
// 7.提交或回滚事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// 编程式事务的处理过程(略)
}
获取事务属性
@Transactional有以下属性:
主要关注的字段:
- rollbackFor:发生xx异常执行回滚,默认是RuntimeException和Error
- propagation:传播行为(默认是required、requiresNew、support等)
- isolation:隔离级别(读未提交、读已提交、可重复读、串行化)
获取事务管理器
事务管理器:TransactionManager,保存着当前数据源连接,提供数据库事务的相关操作方法,包括开启、挂起和回滚事务。一个数据源需要一个事务管理器。
事务管理器的获取过程:
- 先从@Transactional的属性配置中获取;
- 如果注解没有指定事务管理器,则从spring容器找到一个实现PlatformTransactionManager接口的实例对象。如果有多个实现该接口的实例对象,同时没有使用@Primary标注的话,就会报错
收集事务信息(重要)
代码入口是:TransactionAspectSupport#createTransactionIfNecessary
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 省略部分代码
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 获取事务状态
status = tm.getTransaction(txAttr);
}
// 省略部分代码
}
// 准备一个事务信息(TransactionInfo)
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
主要分析tm.getTransaction(txAttr)方法,处理过程如下:
- 获取事务对象(DataSourceTransactionObject)
- 判断当前线程是否已经开启事务,如果是的话,则根据传播行为进行相应处理
- 如果没有开启事务,则构建事务状态,开始新事务
详细代码如下:
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
// 1.获取事务对象
Object transaction = doGetTransaction();
// ... 省略部分代码
// 2.判断当前线程是否开启事务
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 3.存在事务,则按照传播行为执行不同操作
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// ... 省略部分代码
// 4.当前线程没有开启事务,即第一次开启事务,执行下面的代码,根据传播行为处理
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
// 传播行为是强制,由于没有开启事务,所以抛出异常
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 构建事务状态
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开始事务
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
// ... 省略部分代码
}
获取事务对象
org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 从TransactionSynchronizationManager中根据当前数据源获取到一个持有数据库连接的对象 ConnectionHolder
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
TransactionSynchronizationManager是每个线程的事务同步管理器,用来管理事务用到的相关资源。在这个类中,我们可以看到很多ThreadLocal变量
// 记录了当前线程中的一些事务资源,已知的有根据DataSource作为key,数据库链接作为value,在执行doBegin操作时会写入新的键值对
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");
判断当前线程是否开启事务
判断规则有两个:
- 当前线程的事务对象是否持有ConnectionHolder
- ConnectionHolder的事务是否处于激活状态
详细代码如下:DataSourceTransactionManager#isExistingTransaction
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
如果当前线程不存在事务,开始新事务
开启事务需要执行以下几步:
- 构建事务状态(TransactionStatus)
- 执行doBegin操作
- 将当前事务的相关属性设置到ThreadLocal,比如隔离级别、传播行为
先来介绍TransactionStatus,当事务管理器对事务进行提交或回滚操作时,需要用到该对象。主要记录的内容包括:
- 当前事务是否为一个新事务,还是加入到别人的事务。(required 和 requires_new需要用到)
- 是否有保存点
- 是否已经设置回滚标志位。(设置回滚标志位,并不会真正执行回滚,而是根据传播行为决定何时回滚以及是否真正回滚)
- 事务是否已经完成
在获取到事务状态后,执行doBegin操作,主要做4件事情:
- 根据数据源对象获取一个数据库连接
- 设置隔离级别
- 执行
con.setAutoCommit(false),开启事务 - 将这个数据库连接con绑定到当前线程。DataSource作为key,con作为value,将这个键值对保存到ThreadLocal的一个Map
最后,把事务状态、事务管理器、事务属性封装成一个TransactionInfo对象。
如果当前线程已经存在事务
主要讨论常用的requires_new和required的区别
/**
* Create a TransactionStatus for an existing transaction.
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// ... 省略其他传播行为
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// 挂起事务
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 似曾相识的代码,开启新事务
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 执行到这里,说明传播行为是support 或者 required
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
// ... 省略部分代码
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
从上面的代码可以看到,当隔离级别为requires_new时,先挂起一个事务然后,然后开启一个新事务。当隔离级别为support或者required时,直接返回,没有挂起或者doBegin操作。
注意:当传播行为是required时,隔离级别不会改变,仍然是上个事务的隔离级别。
异常处理
当收集完事务信息后,就执行目标方法,如果执行过程中发生异常,根据@Transactional配置的rollbackFor指定的异常,决定回滚还是提交事务。
/**
* Handle a throwable, completing the transaction.
* We may commit or roll back, depending on the configuration.
* @param txInfo information about the current transaction
* @param ex throwable encountered
*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// ...省略部分代码
// 如果抛出的异常是指定的回滚异常,或者默认值,则执行回滚。否则,提交事务
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
// ...省略部分代码
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
// ...省略部分代码
}
}
}
提交事务
当没有异常发生,根据是否设置了回滚标志位,决定是回滚还是提交事务。
/**
* Execute after successful completion of call, but not after an exception was handled.
* Do nothing if we didn't create a transaction.
* @param txInfo information about the current transaction
*/
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
/**
* This implementation of commit handles participating in existing
* transactions and programmatic rollback requests.
* Delegates to {@code isRollbackOnly}, {@code doCommit}
* and {@code rollback}.
* @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
* @see #doCommit
* @see #rollback
*/
@Override
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 设置了回滚标志位,则执行回滚
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
// ... 省略部分代码
// 提交事务
processCommit(defStatus);
}
processCommit
在真正执行提交的地方,需要判断当前事务是否为新事务,是的话,才执行提交操作。
/**
* Process an actual commit.
* Rollback-only flags have already been checked and applied.
* @param status object representing the transaction
* @throws TransactionException in case of commit failure
*/
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
// 存在保存点,释放保存点
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
// 新事务则提交
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
// ...省略部分代码
}
finally {
cleanupAfterCompletion(status);
}
}
processRollback
回滚的过程是,如果有保存点,则会回滚到保存点。如果是新事务,则直接执行回滚操作。如果加入到其他事务中,不执行回滚,而是设置回滚标志位。
/**
* Process an actual rollback.
* The completed flag has already been checked.
* @param status object representing the transaction
* @throws TransactionException in case of rollback failure
*/
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
// 有保存点,回滚到保存点
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
// 新事务,直接回滚
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// 加入到其他事务,设置回滚标志位
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
// ... 省略部分代码
finally {
cleanupAfterCompletion(status);
}
}