事务基于@Transactional注解和AOP(动态代理)
1. 动态代理失效
对于基于接口动态代理的 AOP 事务增强来说,由于接口的方法都必然是 public 的,这就要求实现类的实现方法也必须是 public 的(不能是 protected、private 等),同时不能使用 static 的修饰符。所以,可以实施接口动态代理的方法只能是使用 public 或 public final 修饰符的方法,其他方法不可能被动态代理,相应的也就不能实施 AOP 增强,换句话说,即不能进行 Spring 事务增强了。
基于 CGLib 字节码动态代理的方案是通过扩展被增强类,动态创建其子类的方式进行 AOP 增强植入的。由于使用 final、static、private 修饰符的方法都不能被子类覆盖,相应的,这些方法将无法实施 AOP 增强。所以方法签名必须特别注意这些修饰符的使用,以免使方法不小心成为事务管理的漏网之鱼。
2. AOP失效
第一种(要增强的类或方法没被spring管理)
对于传统的web项目来说,通常使用spring和springmvc,因此对于这种项目来讲,他是有两个容器的,一个是spring容器,一般我们会把Service层的东西注入到spring容器中,另一个是springmvc的容器,通常这个容器里注入的是Controller层的东西,这里我们认为spring容器是父容器,springmvc是子容器的概念,然后我们大家都知道通过父子继承关系可知,子容器是可以读取到父容器中的东西,但是父容器是无法读到子容器中的内容,因此基于这个场景,有的同学,把Aop的实现类注入到了spring容器中,并且将Aop的切点表达式配置aop:config <aop:pointcut的execution也配到了spring容器的xml,而巧了这位同学要切的类方法,正好是Controller,也就是springmvc容器中的东西,那么这时候问题就来了,aop在初始化时会在自己的容器中寻找能够匹配的类方法,然后给他套上一层代理,此时他在自己能够访问到的spring容器中根本找不到与之匹配的类和方法,因为这些类和方法是在springmvc容器中管理的,因此就没有代理成功
解决:那么对于上述问题要怎么修改呢?只需要确保你要Aop切的类和方法与你Aop配置切点aop:config <aop:pointcut的execution表达式声明是在同一个容器中即可,此时只需要将这个配置移到springmvc容器的xml中即可
第二种(要被增强的类或方法同时被spring和springMVC扫描)
第二种情况与第一种情况有些许的类似,但并不相同,是关于重复扫描的,比如你在spring容器中配置了一个Aop,并且把他托管给spring容器管理,而且execution表达式切的也是spring容器中管理的类和方法,理论上这个时候是好用的,这批execution切到的类都被加了代理,但是巧了,springmvc容器中由于配置的是包路径扫描,恰好把execution表达式切的这一批对象又扫了一遍,又都托管给了springmvc容器,而此时扫到的这批对象,是重新new出来交给springmvc管理的,因此并没有被aop代理,所以在使用时,注入进来的可能是springmvc容器管理的这批对象,因此使用时发现Aop代理失效了
解决:这个问题的解决方案,就是避免两个容器重复扫描。
第三种(要增强的方法被同类的方法调用)
第三个问题就比较简单了,他的现象是有些方法被Aop代理成功了,但有个别方法没有代理成功,究其原因发现这部分没有代理成功的方法并不是通过代理对象调用的,而是自身调用的,故被调用的方法没有被Aop代理,无法织入横切逻辑。
解决:举个栗子,比如A.a(),A.b()是被代理的类和方法,那么当我调用A.a()时,此时a被代理了,成功执行代理类的内容,但还没有完,a()方法中调用了自身的方法b(),此时我们以为b也会被代理类代理,但实际上并没有,因为他是自身方法调用了并不是通过代理类A调用的,在a方法中可以通过((A) AopContext.currentProxy()).b()调用,不过前提是配置上<aop:config expose-proxy="true">,expose-proxy="true"是重点(先开启cglib代理,开启 exposeProxy = true,暴露代理对象)
第四种(用的注解方式没有开启注解支持)
这种就更好理解了,只需要开启注解支持就行了。
xml方式开启在spring配置文件添加上aop:aspectj-autoproxy/即可
注解方式开启在启动类上添加@EnableAspectJAutoProxy即可
总结
第一种和第二种是因为MVC分层没有用好,接收数据和业务逻辑要分开,
第三种是因为代理模式限制
第四种就是使用不当了
3. @Transactional失效
事务基于注解和AOP,注解失效和AOP失效
原因一:没有开启事务管理
原因二:标注了@Transactional的方法里面的异常被捕获了
原因三:标注了@Transactional的方法发生了非Error 或者 非RuntimeException
原因四:标注了@Transactional的方法的事务传播类型propagation配置成了NOTSUPPORT
原因五:标注了@Transactional的方法的事务传播类型propagation配置成了NEVER
原因六:标注了@Transactional的方法的事务传播类型propagation配置成了SUPPORTS且当前没有事务
原因七:外部调用方法A,A内部调用方法B,A没有@Transaction注解而B有@Transactional注解
原因九:标注了@Transactional的方法不是public的
原因十:标注了@Transactional的方法发生的异常不是rollbackFor指定的类型或子类
原因十一:数据库不支持事务