避坑:@Around与@Transactional混用导致事务不回滚
前言 上个月,同事出于好奇在群里问AOP的环绕通知与事务注解混合用会不会导致出现异常不回滚的情况。这个问题我一下子回答不上来,因为平时没这样用过,在好奇心的驱使下,我调试了半天终于得到结果,今天我就展开讲讲。(源码解读在最后面,感兴趣的可以看看。) 结论 首先告诉大家的是,同时使用AOP环绕通知和事务注解之后,最终生成的拦截器链的相对顺序是事务的拦截器在前面,AOP环绕通知的拦截器在后面。 在事务的实现中将拦截器的执行过程包裹在了try-catch块中,发生异常后根据配置来决定是否回滚事务。(详见org.springframework.transaction.interceptor.TransactionInterceptor#invoke),因此事务后面的拦截器都会影响事务的执行结果。如果在AOP环绕通知里面将拦截器链执行结果中的异常给吞掉,那么事务就会正常提交而不会回滚。 示例 业务代码 业务代码中直接抛出异常,代码如下所示。 java content_copy 复制代码 package com.example.demo.aspect; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @Author Paul * @Date 2022/7/3 15:52 */ @Service public class CustomService { @Transactional(rollbackFor = Exception.class) public void echo(){ boolean s = true; if (s){ throw new RuntimeException("test"); } System.out.println("Hello------"); } } 环绕通知 环绕通知中捕捉异常并打印日志,代码如下所示。 java content_copy 复制代码 package com.example.demo.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * @Author Paul * @Date 2022/7/3 15:49 */ @Component @Aspect public class CustomAspect { @Pointcut("execution(* com.example.demo.aspect..*(..))") public void pointcut(){ } @Around("pointcut()") public void around(ProceedingJoinPoint joinPoint){ try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } } } 测试类 这里是用get请求来测试(本来应该用 unit test 来测试的,但是懒得写代码了,手动测试和 UT 的效果一样),代码如下所示。