本文介绍下@Transactional底层实现和哪些场景会导致其失效
当使用@Transactional注解标注一个方法时,springboot会在运行时生成一个代理对象,该代理对象拦截被注解的方法调用,并在方法调用前后进行事务管理。事务管理包括开启事务、提交事务或者回滚事务等操作。
@Transactional实现思路如下:
@Transactional部分实现源码如下
简单理解就是利用生成一个代理对象,然后去执行方法,如果出现异常那就自动回滚,否则就自动提交事务
导致事务失效的场景有如下几个:
1、非public修饰的方法;
2、代码中使用try/catch处理的异常;
3、调用类内部的@Transactional方法;
4、数据库不支持事务。
解释下各个失效场景的原因:
1、非public修饰的方法:
① 浅层原因是@Transactional源码限制了必须是public才能执行后续代码流程,源码如下:
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) { if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // 后续代码省略
所以@Transactional修饰方法是public才行
② 深层次原因
深层次原因是 springboot动态代理只能代理公共方法,而不能代理私有方法或者受保护的方法。动态代理通常使用的是JDK动态代理和CGLib代理。JDK动态代理是基于接口实现的,基于接口实现的,接口方法也是需要public方法。CGlib是基于继承类,生成子类的,父类方法也是需要public。
所以动态代理底层机制所限,spring动态代理只能代理公共方法。
解决办法:使用public修饰方法
2、为什么代码中使用try-catch,事务不回滚了
是因为@Transactional在底层实现中,会捕捉异常,如果有了异常才有回滚,而程序中如果try-catch之后,它底层就感应不到异常,也就不会回滚事务,可以看下上面的底层源码实现
解决办法:要么就在catch中自己手动回滚代码(TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();)。或者业务代码模块可以catch捕捉异常,但是你要继续往外抛,能让它底层捕捉到异常,那才能回滚。
3、调用类内部的@Transactional方法为什么使事务失效
因为@Transactional是基于动态代理实现的,而调用类内部方法时是this对象实现的,这样就绕过了代理对象,从而事务失效了。
解决办法:可以在内部类上也添加@Transactional注解,这样就可以利用springboot隔离级别,让事务生效。
标签:事务,Transactional,代理,回滚,失效,方法,public,底层 From: https://www.cnblogs.com/qwg-/p/18080283