使用AopContxt.currentProxy()方法可获取当前类的代理对象(是通过StaticUnadvisedExposedInterceptor或DynamicUnadvisedExposedInterceptor或JdkDynamicAopProxy#invoke中设置的,@EnableAspectJAutoProxy#exposeProxy可设置为true,将当前的aop对象暴露到AopContext的ThreadLocal变量currentProxy中)
在使用@Transactional注解声明事务时,会有以下四种情况:
-
在不同类中,事务方法A调用非事务方法B,事务具有传播性,事务生效;
-
在不同类中,非事务方法A调用事务方法B,事务生效;
-
在同一个类中,事务方法A调用非事务方法B,事务生效;
-
在同一个类中,非事务方法A调用事务方法B,事务失效,这是由于使用Spring AOP代理造成的,只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
解决办法:
- 采用AopContext.currentProxy().方法B名()来进行调用
- ApplicationContext.getBean()
- 在当前类中注入自己
- 使用手动事务
如果该类存在@Async异步任务方法,那么@Async方法应该使用第3种方式并且在引入的自身代理对象上加上@Lazy注解,让其再进行代理封装(看这里: Spring AOP入门以及基于XML的Spring AOP配置的深入学习与使用【两万字】_刘Java的博客-CSDN博客)
思考:为什么在被代理类的方法中调用被代理的其它方法时,不经过增强方法呢?
这是由aop的实现机制决定的,spring aop抽象出了TargetSource接口(从中可以获取target目标对象),当外部通过调用代理对象的方法时,就会经过MethodInterceptor方法拦截器(增强方法),当所有的方法拦截器都调用完了(责任链模式),就会从代理配置(ProxyFactory->ProxyCreatorSupport->AdvisedSupport中维护targetSource)的TargetSource中获取到目标对象,然后使用反射调用该目标对象的目标方法,也就是最终目标对象执行时,其实这个时候已经不是代理对象了,此时是目标对象!那么反射方法执行时,方法中的this,就是目标对象,那在一个成员方法中调用类中的另外一个成员方法(此时省略了this),省略的这个this是目标对象,而不是代理对象,因此被调用的另外一个成员方法时,是不会经过增强方法的(方法拦截器)。