目录

Spring源码分析事务下

Spring源码分析の事务(下)


前言

首先回顾一下上篇中的内容,Spring管理的事务,可以分为四个步骤:

https://i-blog.csdnimg.cn/direct/b093b9412034419b8c30a85a6a580d15.png 本篇笔记记录 提交事务回滚 相关源码实现。

一、提交事务

在提交事务的方法中,还会对于事务的信息进行判断,涉及到 本地回滚全局回滚 两种情况:

  1. 本地回滚指的是事务代码内部显式地调用 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();
    }
}
  1. 全局回滚 指的是事务管理器检测到某些规则,标记整个事务为必须回滚,即使代码本身仍然尝试提交事务,例如:
@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 是正常执行提交事务的逻辑:

https://i-blog.csdnimg.cn/direct/2cea69fd5edf462f97e12335ab9c14a3.png 涉及到TransactionSynchronization的一些回调

https://i-blog.csdnimg.cn/direct/ba29f4dfea3148db99cb59de321ee8e7.png 拿到连接,真正执行对应数据库的提交事务

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("业务逻辑执行完毕,等待事务提交或回滚...");
    }
}

二、回滚事务

在回滚事务的代码中,会对于异常的类型进行判断,如果没有给 @TransactionalrollbackFor 属性设置异常类型,则默认只会对 RuntimeExceptionError 类型的异常进行回滚:

https://i-blog.csdnimg.cn/direct/e4c991ceb24d406d90be1e050d7e7c08.png https://i-blog.csdnimg.cn/direct/d6ae295cb4924b189309bea00a91f198.png

如果异常不符合回滚条件,事务依旧会提交。

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;
            }
        }
    }
}

https://i-blog.csdnimg.cn/direct/ef238499f8d04426859da2fd7b63f9a0.png 在回滚事务时,依旧会触发一些回调方法,并且最终底层调用的是对应数据库的回滚事务的实现