起因
最近有个实习生开发了一个job,这个job提测之后,测试那边很快反映有数据异常,看了下log发现已有几条异常信息,但我惊讶的是有数据不一致的问题。我浏览了一下代码,并没有发现什么问题,发现都有用Transactional和rollback声明(当时脑子有点短路,一时没有看出来),但直觉告诉我这种错误八成与事务处理不当有关。
既然如此只好debug一下,接着很快便知道了。
代码大概是这样的
示例代码:
@Transactional(rollbackFor = Exception.class) |
如上,methodB()中有一个持久化的操作,在那之后有可能会抛异常(th()方法)。
实习生的想法是,methodB()加一个注解@Transactional(rollbackFor),想让methodB执行失败时回滚事务。
然而,这种是对spring aop不了解导致的错误。
解释
我对实习生说了这个错误之后,他却坚持这样是有效的。。没办法,只好以理服人。断点debug,翻源码。
如上图,可看到methodB()的调用栈,发现methodMaster()是经过拦截器后调用的(Cglib生成代理对象的情况下,AOP拦截和回调可在DynamicAdvisedInterceptor.intercept()
方法中找到,如下)。
@Override |
现在在来看methodB():
很明显,methodB是在methodMaster()中调用的,TransactionInterceptor
并没有拦截,因此@Transactional是无效的。
现在来对比一下,切换启用位置B代码(使用memberOrderCopyService.methodB(status)
)的执行情况:
从上图可知,此时memberOrderCopyService.methodB()和原先的memberOrderService.methodMaster()都经过了TransactionInterceptor,然后通过代理invoke,这种是正确的,当有异常出现,事务也会正常回滚。
其实,这是一个简单的问题,通过推理也可以猜到,而且如果内部每个方法都用拦截器,会是一个很大的性能问题。