Spring源码分析事务下
目录
Spring源码分析の事务(下)
前言
首先回顾一下上篇中的内容,Spring管理的事务,可以分为四个步骤:
本篇笔记记录
提交事务
和
回滚
相关源码实现。
一、提交事务
在提交事务的方法中,还会对于事务的信息进行判断,涉及到
本地回滚
和
全局回滚
两种情况:
- 本地回滚指的是事务代码内部显式地调用 setRollbackOnly(),导致事务只能回滚,不能提交,以下两种情况的案例都有可能会触发:
@Transactional
public void updateAccount() {
try {
accountDao.debit("userA", 100);
accountDao.credit("userB", 100);
// 业务逻辑检测到异常情况,主动要求回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} catch (Exception e) {
// 处理异常
}
}
@Transactional
public void transferMoney() {
try {
accountDao.debit("userA", 500);
accountDao.credit("userB", 500);
} catch (SQLException e) {
// 捕获异常但不抛出,事务本身仍需回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
- 全局回滚 指的是事务管理器检测到某些规则,标记整个事务为必须回滚,即使代码本身仍然尝试提交事务,例如:
@Transactional
public void processOrder() {
createInvoice(); // 调用方法,内部事务发生异常
shipOrder();
}
@Transactional(propagation = Propagation.REQUIRED)
public void createInvoice() {
if (invoiceDataInvalid()) {
throw new RuntimeException("Invalid invoice data");
}
}
在进行上述两种情况的判断之后,会正常执行提交事务逻辑:
@Override
@Override
public final void commit(TransactionStatus status) throws TransactionException {
// 1. 事务已完成,不能重复提交或回滚
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
// 2. 转换为 DefaultTransactionStatus 类型,获取事务的详细状态信息
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 3. 如果当前事务被标记为 "仅回滚",则执行回滚操作而非提交
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
// 4. 如果事务全局标记为 "仅回滚" 且不允许提交,则执行回滚
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
}
// 5. 正常执行提交事务逻辑
processCommit(defStatus);
}
processCommit
是正常执行提交事务的逻辑:
涉及到TransactionSynchronization的一些回调
拿到连接,真正执行对应数据库的提交事务
TransactionSynchronization
的回调,可以自定义实现:
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class MyTransactionSynchronization implements TransactionSynchronization {
@Override
public void beforeCommit(boolean readOnly) {
System.out.println("事务提交前执行逻辑...");
if (readOnly) {
System.out.println("当前事务是只读的");
}
}
@Override
public void afterCommit() {
System.out.println("事务提交后执行逻辑,比如清理缓存或发送通知...");
}
@Override
public void afterCompletion(int status) {
if (status == STATUS_COMMITTED) {
System.out.println("事务成功提交");
} else if (status == STATUS_ROLLED_BACK) {
System.out.println("事务已回滚,执行补偿逻辑...");
} else {
System.out.println("事务状态未知...");
}
}
}
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Transactional
public void executeBusinessLogic() {
System.out.println("执行业务逻辑...");
// 在事务中注册回调
TransactionSynchronizationManager.registerSynchronization(new MyTransactionSynchronization());
// 业务逻辑结束
System.out.println("业务逻辑执行完毕,等待事务提交或回滚...");
}
}
二、回滚事务
在回滚事务的代码中,会对于异常的类型进行判断,如果没有给
@Transactional
的
rollbackFor
属性设置异常类型,则默认只会对
RuntimeException
或
Error
类型的异常进行回滚:
如果异常不符合回滚条件,事务依旧会提交。
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
// 判断事务信息是否为空,并且事务状态是否存在
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// 记录日志,打印当前事务的标识信息以及抛出的异常
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
}
// 检查事务属性是否需要对该异常进行回滚
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 需要回滚事务,调用事务管理器的 rollback 方法
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} catch (TransactionSystemException ex2) {
// 事务回滚过程中发生系统异常,记录日志,并包装原始异常
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
} catch (RuntimeException | Error ex2) {
// 事务回滚过程中发生运行时异常或错误,记录日志,并抛出新的异常
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
} else {
// 该异常不符合回滚条件,需要提交事务
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
} catch (TransactionSystemException ex2) {
// 事务提交过程中发生系统异常,记录日志,并包装原始异常
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
} catch (RuntimeException | Error ex2) {
// 事务提交过程中发生运行时异常或错误,记录日志,并抛出新的异常
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
在回滚事务时,依旧会触发一些回调方法,并且最终底层调用的是对应数据库的回滚事务的实现