Spring 事务回滚机制详解
阅读原文时间:2021年09月02日阅读:1

1:事务原理

类路径:org/springframework/aop/framework/CglibAopProxy.java


ReflectiveMethodInvocation#proceed 后续:



org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

    /**
     *  每个被 @Transactional 修饰的方法都会走一遍 transaction interceptor,然后新增一个事务节点。
     *  每个事务节点执行前都会判断是否需要新建事务、开启事务。
     **/
    @Nullable
    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();
        final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // 创建一个事务信息对象,每一次调用 @Transactional 注解修饰的方法,都会重新创建一个 TransactionInfo 对象。
            // 若有调用链有多个 @TransactionInfo 修饰的方法,则会形成一个事务链。
            // 将最新的事务节点信息通过 threadLocal 更新到当前线程 。
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

            Object retVal;
            try {
                // 真正执行crud语句的过程
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // 抛异常之后决定是否回滚还是继续提交
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                // 清除当前节点的事务信息,将旧事务节点信息通过 threadLocal 更新到当前线程。
                cleanupTransactionInfo(txInfo);
            }
            // 事务链执行完毕之后
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }else {
            ...
        }
    }

org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo

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

2:事务回滚场景

用两个Service进行测试:

/**
 * 模拟 Service A
 **/
@Service
public class AopiService {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Resource(name = AopiRepositry.PACKAGE_BEAN_NAME)
    private AopiRepositry aopiRepositry;
    @Resource
    private PmsTestService pmsTestService;
    @Resource
    private AopiService aopiService;

    ...
}

/**
 * 模拟 Service B
 **/
@Service
public class PmsTestService {

    @Transactional(rollbackFor = Exception.class)
    public void insertWithTransactional() {
        int i = 1 / 0;
    }

    public void insertWithoutTransactional() {
        int i = 1 / 0;
    }
}

模拟场景:

1:模拟ServiceA调用ServiceB(会异常,被try-catch),这种情况会回滚吗?

2:模拟ServiceA中调用自己的本类中的方法(会异常,被try-catch),这种情况会回滚吗?

3:模拟ServiceA注入自己并通过依赖的ServiceA调用另一个方法(会异常,被try-catch),这种情况会回滚吗?


    /**
     * serviceA 加了 @Transactional
     * serviceB 加了 @Transactional
     * 最终:回滚
     **/
    @Transactional(rollbackFor = Exception.class)
    public void insertA() {
        Aopi aopi = new Aopi();
        aopi.setName("1");
        aopi.setAge(23);
        aopiRepositry.insert(aopi);
        try {
            pmsTestService.insertWithTransactional();
        } catch (Exception e) {
            log.error("插入报错", e);
        }
        // 判断事务是否回滚
        if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
            log.info("事务回滚了");
        } else {
            log.info("事务没回滚");
        }
    }

    /**
     * serviceA 加了 @Transactional
     * serviceB 没加 @Transactional,但是手动 throw e;
     * 最终:回滚
     **/
    @Transactional(rollbackFor = Exception.class)
    public void insertAA() {
        Aopi aopi = new Aopi();
        aopi.setName("1");
        aopi.setAge(23);
        aopiRepositry.insert(aopi);
        try {
            pmsTestService.insertWithoutTransactional();
        } catch (Exception e) {
            log.error("插入报错", e);
            throw e;
        }
    }

    /**
     * serviceA 加了 @Transactional
     * serviceB 没加 @Transactional,但是手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
     * 最终:回滚
     * <p>
     * 若不手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),那么不会回滚
     **/
    @Transactional(rollbackFor = Exception.class)
    public void insertAAA() {
        Aopi aopi = new Aopi();
        aopi.setName("1");
        aopi.setAge(23);
        aopiRepositry.insert(aopi);
        try {
            pmsTestService.insertWithoutTransactional();
        } catch (Exception e) {
            log.error("插入报错", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        // 判断事务是否回滚
        if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
            log.info("事务回滚了");
        } else {
            log.info("事务没回滚");
        }
    }

    /**
     * serviceA 加了 @Transactional
     * 调用过程中被异常被捕获,并不抛出
     * 最终:不回滚
     **/
    @Transactional(rollbackFor = Exception.class)
    public void insertAAAA() {
        Aopi aopi = new Aopi();
        aopi.setName("1");
        aopi.setAge(23);
        aopiRepositry.insert(aopi);
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            log.error("插入报错", e);
        }
        // 判断事务是否回滚
        if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
            log.info("事务回滚了");
        } else {
            log.info("事务没回滚");
        }
    }

    /**
     * 本类方法A 加了 @Transactional
     * 本类方法B 加了 @Transactional,异常被捕获,并不抛出
     * 最终:不回滚
     * <p>
     * 原因:调用 insert 并不会重新走代理调用(this 对象不是代理对象)
     **/
    @Transactional(rollbackFor = Exception.class)
    public void insertAAAAA() {
        Aopi aopi = new Aopi();
        aopi.setName("1");
        aopi.setAge(23);
        aopiRepositry.insert(aopi);
        try {
            insert();
        } catch (Exception e) {
            log.error("插入报错", e);
        }
        // 判断事务是否回滚
        if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
            log.info("事务回滚了");
        } else {
            log.info("事务没回滚");
        }
    }

    /**
     * 本类方法A 加了 @Transactional
     * 自己注入自己,再调用本类方法B,加了 @Transactional,异常被捕获,并不抛出
     * 最终:回滚
     * <p>
     * 原因:aopiService bean 是一个代理bean,每次调用都会重新走代理调用逻辑。
     **/
    @Transactional(rollbackFor = Exception.class)
    public void insertAAAAAA() {
        Aopi aopi = new Aopi();
        aopi.setName("1");
        aopi.setAge(23);
        aopiRepositry.insert(aopi);
        try {
            aopiService.insert();
        } catch (Exception e) {
            log.error("插入报错", e);
        }
        // 判断事务是否回滚
        if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
            log.info("事务回滚了");
        } else {
            log.info("事务没回滚");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void insert() {
        int i = 1 / 0;
    }

3:结论

1:程序异常,事务是否回滚取决于 当前线程的事务状态。

2:异常是否抛出并不是并不是一定会导致回滚失败的原因。即使异常被捕获,且不再次throw,事务也可能回滚。

3:抛出的异常不在rollback 范围内,也不会进行回滚。


其他:

1:spring 用的cglib代理库是自己的库(依赖于spring-aop的包),并没有引用第三方cglib库。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章