zoukankan      html  css  js  c++  java
  • Spring 事务回滚机制详解

    1:事务原理

    1.1:aop/动态代理

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

    drawing

    ReflectiveMethodInvocation#proceed 后续:

    drawing

    作用:采用aop/动态代理的作用是为了在调用@Transactional 注解修饰的方法之前,对目标方法做一次增强。


    1.2:threadLocal

    drawing

    作用:采用ThreadLocal的作用是用来存储当前线程的事务节点信息。


    1.3:事务核心代码

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

    1.4:事务链-图

    drawing

    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),这种情况会回滚吗?


    2.1:场景1-1

        /**
         * 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("事务没回滚");
            }
        }
    

    2.2:场景1-2

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

    2.3:场景1-3

        /**
         * 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("事务没回滚");
            }
        }
    

    2.4:场景2-1

        /**
         * 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("事务没回滚");
            }
        }
    

    2.5:场景2-2

        /**
         * 本类方法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("事务没回滚");
            }
        }
    

    2.6:场景3-1

        /**
         * 本类方法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库。

  • 相关阅读:
    算法——基础
    递归函数
    docker常用命令
    vue——计算属性和侦听器
    vue——指令系统
    vue——介绍和使用
    webpack、babel模块、模块化
    Nodejs介绍及npm工具使用
    ECMAScript 6简介
    easyui(入门)
  • 原文地址:https://www.cnblogs.com/zgq7/p/15214423.html
Copyright © 2011-2022 走看看