前言:
今天朋友们对“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事务回滚