让Spring事务支持同一个类的内部调用

问题:当同一个类中的方法A调用方法B时,即使两个方法都打上了@Transactional注解,方法B的事务也不会生效。

原因:默认情况下Spring事务是基于代理的,也就意味着获取到的service对象是代理后的对象(class com.sun.proxy.$Proxy,基于接口的情况)。当外部调用该对象上的方法时,经过aop加上的事务逻辑后,最终会进入到目标对象(即原始的service对象)的方法逻辑,此时在方法内部再调用自己的另一个方法B,本质上就是在原始对象上进行调用,此时自然而然不会牵扯到任何aop的事务逻辑。

解决办法:由上而知,采用代理的方式肯定行不通,故而必须采用字节码提升的方式,直接修改类的字节码,为每个打上@Transactional注解的方法都加上事务逻辑。具体分两步走:

       1. 设置 @EnableTransactionManagement(mode = AdviceMode.ASPECTJ) ,表示采用Aspectj 来对需要事务管理的方法进行字节码提升。

       2. 首先引入           

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.0</version>
</dependency>

      加入spring对Aspectj的支持。

      而Aspectj weaving 的方式有两种:编译时和运行时。

      编译时即在编译期间就完成了对类的字节码的修改。可以通过加入一个maven插件实现:

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.6</version>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <!-- <goal>test-compile</goal> -->
                    </goals>
                </execution>
            </executions>
            <configuration>
                <complianceLevel>1.8</complianceLevel>
                <forceAjcCompile>true</forceAjcCompile>
                <weaveDirectories>
                    <weaveDirectory>${project.build.outputDirectory}</weaveDirectory>
                </weaveDirectories>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
            </configuration>
        </plugin>
    </plugin>
</build>

   运行时即在class被加入到ClassLoader之前动态的修改类的字节码。

   具体用到了LoadTimeWeaving,详情参考Spring官方文档  https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-aj-ltw

 

补充:Foo类中方法A调用Bar类中的方法B是ok的,因为是在代理后的Bar的对象上进行操作,事务逻辑是存在的。但是要注意方法B的事务的传播级别,默认为REQUIRED,即如果存在了一个事务则直接使用,不存在才会创建一个新的事务。所以加入方法A的事务是READ-WRITE,方法B的事务是READ-ONLY,这时方法B会直接使用方法A的事务,会导致READ-ONLY失效。这时需要将方法B的传播级别改为REQUIRES_NEW。

 

建议: 强烈建议同一个类内部的事务方法之间不要进行相互调用,尽可能的重构此类代码,最好合并到一个事务方法中。这也是Spring官方推荐的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值