龙空技术网

spring的事务回滚机制你懂得多少?

奋斗喝咖啡 99

前言:

今天朋友们对“java事务回滚”都比较关注,小伙伴们都需要了解一些“java事务回滚”的相关内容。那么小编也在网络上汇集了一些有关“java事务回滚””的相关内容,希望姐妹们能喜欢,咱们快快来学习一下吧!

1:事务原理1.1:aop/动态代理

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

ReflectiveMethodInvocation#proceed 后续:

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

1.2:threadLocal

作用:采用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:事务链-图2:事务回滚场景

用两个Service进行测试:

/** * 模拟 Service A **/@Servicepublic 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 **/@Servicepublic 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库。

学习更多JAVA知识与技巧,内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、SpringBoot、SpringCloud、RabbitMQ、Kafka、Linux等技术栈,关注与私信博主(888)

标签: #java事务回滚