同一个类调方法导致注解式事务失效的问题
说明
在项目启动时,如果有@Transactional
注解,spring是生成代理类来开启事务、提交事务和回滚事务的,但是如果是同一个类之间调用,就是直接调方法,导致事务失效。我们可以用一下方式在同一类获取当前类的代理对象,从而避免事务失效的问题。但是这种方式会有问题,下面有说明。推荐调用拆到两个类中
代码
- 在启动类开启
@EnableAspectJAutoProxy(exposeProxy = true)
- 使用
handle()上有@Transactional
事务注解
AService aService = (AService ) AopContext.currentProxy();
WebResult webResult = aService.handle();
使用上述方法需要注意的点
- 需要确保当前方法是通过代理对象调用的,而不是直接通过类的实例调用的。否则,AopContext.currentProxy()将返回null。
- 需要在Spring容器中开启了exposeProxy配置,即在@EnableAspectJAutoProxy注解中设置exposeProxy属性为true。否则,AopContext.currentProxy()将返回null。
- 需要注意潜在的性能影响。每次调用AopContext.currentProxy()都会涉及到AOP拦截器链的处理,可能会对性能产生一定的影响。因此,建议在必要的情况下使用,并进行性能测试和评估。
总之,使用AopContext.currentProxy()可以解决同一个类调用方法导致注解式事务失效的问题,但需要注意上述提到的使用条件和潜在的性能影响及可能会破坏Spring AOP的默认行为。
破坏的Spring AOP的默认行为
破坏Spring AOP的默认行为主要包含以下几种方式:
- 强制使用CGLIB代理:在Spring AOP中,默认情况下,如果被代理的对象没有实现接口,会使用CGLIB来创建代理对象。但是,如果你强制使用CGLIB代理,即使被代理的对象已经实现了接口,也会使用CGLIB来创建代理对象,破坏了Spring AOP的默认行为。
- 修改默认的代理实现方式:在Spring Boot 2.x中,AOP默认使用CGLIB实现,而在Spring 5.x中,AOP默认使用JDK动态代理。如果你通过配置项spring.aop.proxy-target-class=false来修改默认的代理实现方式,即使是在Spring Boot 2.x中,也会使用JDK动态代理来创建代理对象,破坏了Spring AOP的默认行为。
- 使用AspectJ织入:Spring AOP默认使用基于代理的AOP实现,即Spring会创建一个代理对象,然后将该代理对象织入到目标对象中。如果你使用AspectJ织入,Spring会创建一个Aspect对象,然后将该Aspect对象织入到目标对象中,破坏了Spring AOP的默认行为。
需要注意的是,这些行为并不一定都是负面的。它们有可能帮助解决某些特定的问题。但是,在破坏Spring AOP默认行为之前,一定要考虑清楚是否真的需要这样做,以及可能的副作用是什么。
纸上得来终觉浅,绝知此事要躬行。