@Transactional底层实现和失效场景

本文介绍下@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隔离级别,让事务生效。

 

posted @ 2024-03-18 14:10  多多指教~  阅读(102)  评论(0编辑  收藏  举报