Spring本地事务失效问题
在SpringBoot框架中,当我们需要用到事务时,一般直接在方法上标注@Transactional,这样当方法内发生异常时,整个方法都会回滚。但Spring的事务存在一个问题,假设存在这么一个场景:
1 | public class Service{ |
在spring中,事务的默认传播模式是propagation = Propagation.REQUIRED,即当b()方法需要一个事务,a()方法的事务会默认传递给b()方法,那么当a()方法中任何一处抛出异常,都会连同b()方法一起回滚。但存在的问题是。此时在b()方法中不论对事务做了什么设置,都是无效的,因为用的是a()方法的事务。再看c()方法,声明了propagation = Propagation.REQUIRES_NEW,即使用一个新事务,这样虽然配置能生效,但当a()方法抛出异常时,c()方法不会跟着一起回滚,显然达不到业务需求。
造成上面的原因是,同一个对象内的事务方法互相调用,绕过了代理对象。因为spring中的事务是通过代理对象实现的,那么能不能这样实现:
1 | public class Service{ |
这样通过代理对象调用方法,虽然配置会生效,但这会出现循环依赖问题,显然也不行。
正确的解决方法是使用代理对象来解决:
引入aop
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>在启动类标注@EnableAspectJAutoProxy(exposeProxy = true)
spring aop中有两种代理模式,一种是jdk动态代理,另外一种是cglib代理。
jdk动态代理是
JDK
原生的,不需要任何依赖即可使用,缺点是如果要使用JDK
动态代理,被代理的类必须实现了接口,否则无法代理;JDK
动态代理无法为没有在接口中定义的方法实现代理,假设我们有一个实现了接口的类,我们为它的一个不属于接口中的方法配置了切面,Spring
仍然会使用JDK
的动态代理,但是由于配置了切面的方法不属于接口,为这个方法配置的切面将不会被织入。JDK
动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低。 cglib代理的优点是,使用
CGLib
代理的类,不需要实现接口,因为CGLib
生成的代理类是直接继承自需要被代理的类;CGLib
生成的代理类是原来那个类的子类,这就意味着这个代理类可以为原来那个类中,所有能够被子类重写的方法进行代理;CGLib
生成的代理类,和我们自己编写并编译的类没有太大区别,对方法的调用和直接调用普通类的方式一致,所以CGLib
执行代理方法的效率要高于JDK
的动态代理。 在这里我们使用cglib代理,并配置对外暴露代理对象
做好这些配置后,回到我们最初的场景,这时候我们可以这样做:
1 | public class Service{ |