对于像我这种喜欢滥用AOP的程序员,遇到坑也是习惯了,不仅仅是事务,其实只要脱离了Spring容器管理的所有对象,对于SpringAOP的注解都会失效,因为他们不是Spring容器的代理类,SpringAOP,就切入不了
当然可以使用原生ASPECTJ,不用SpringAOP,但是基于生态链问题,还是尽量使用SpringAOP
这里简单说一下,Spring如何选择使用CGLIB,或者是JDK代理,
简单来说,如果实现了某个接口,那么Spring就选择JDK代理(不一定),如果没有,那么就选择CGLIB代理,说起来简单,Spring还会闹脾气,不代理呢
一直以来比较多的情况是在Controller 调用Service 的方法,把事务直接在Service的方法上,妥妥的没问题,事务正常执行
考虑以下问题: 同对象的方法B 调用自己的方法A,这里的事务将会失效(严格上来说,只要对方法A使用注解AOP均会失效),原因是因为这里的this.调用的并不是Spring的代理对象
@Service
public classClassA{
@Transactional(propagation=Propagation.REQUIRES_NEW)public voidmethodA(){
}/*** 这里调用methodA() 的事务将会失效*/
public voidmethodB(){this.methodA();
}
}
最简单的解决方法为:(代理模式为JDK 的情况下(根据接口代理))
@Servicepublic class ClassA extendsBaseClass{
@Transactional(propagation=Propagation.REQUIRES_NEW)
@Overridepublic voidmethodA(){
}/*** 这里调用methodA() 的事务将会失效*/
public voidmethodB(){//使用getBean
((BaseClass)SpringUtil.getBean("classA")).methodA();
}
}
第二种方式:
解决第一步: 必须
SpringBoot:注解开启cglib代理,开启 exposeProxy = true,暴露代理对象 (否则AopContext.currentProxy()) 会抛出异常
@EnableAspectJAutoProxy(exposeProxy = true)
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
配置方式:配置方式下,我用的是SpringBoot 1.5.4没有找到exposeProxy的选项,所以建议使用注解式.
传统Spring配置文件
XML
第二步(需要保证Spring对这个bean创建了代理对象,基本上涉及到Aop的方法的类,都会创建代理对象) 可以用以下代码判断
/*** Created by laizhenwei on 19:37 2017-10-14*/@Service("classImplProxy")
@Scope(proxyMode=ScopedProxyMode.INTERFACES)public class ClassImplProxy implementsIClass {
@Override
@Transactionalpublic voidsysout() {
}//是否代理对象
@Overridepublic booleanisAopProxy() {returnAopUtils.isAopProxy(AopContext.currentProxy());
}//是否cglib 代理对象
@Overridepublic booleanisCglibProxy() {returnAopUtils.isCglibProxy(AopContext.currentProxy());
}//是否jdk动态代理
@Overridepublic booleanisJdkDynamicProxy() {returnAopUtils.isJdkDynamicProxy(AopContext.currentProxy());
}
}
获取代理对象
@Servicepublic classClassA{
@Transactional(propagation=Propagation.REQUIRES_NEW)public voidmethodA(){
}
public voidmethodB(){
//手动获取此对象Spring的代理类
((ClassA)AopContext.currentProxy()).methodA();}
}
第三种方式
直接在当前类@Autowire 或者@Resource 注入自己,然后用注入的bean 调用方法