• spring声明式事务 同一类内方法调用事务失效


    [问题]

    有一个服务类,其一个声明了事务的方法,里面做了三次插入SQL操作,但是在后面出错回滚时,却发现前面插入成功了,也是说,这个声明了事务的方法,实际上并没有真正启动事务!怎么回事呢?难道Spring的声明式事务失效了?

          

    [探幽]

         其实以前也会碰到有人说,Spring的事务配置不起作用,但是根据第一反应和以往经验,我总会告诉他,肯定是你的配置有问题啦;所以这一次,我想也不会例外,大概是把事务注解配在了接口上而不是实现方法上,或者,如果是用XML声明方式的话,很可能是切入点的表达式没有配对。

         不过,在检查了他们的配置后,却发现没有配置问题,该起事务的实现方法上,用了@Transactional事务注解声明,XML里也配了注解驱动<tx:annotation-driven .../>,配置很正确啊,怎么会不起作用?

         我很纳闷,于是往下问:

         问1:其他方法有这种情况么?

         答1:没有。

         问2:这个方法有什么特别的么(以下简称方法B)?

         答2:就是调后台插了三条记录啊,没啥特别的。

         问3:这个方法是从Web层直接调用的吧?

         答3:不是,是这个Service类(以下简称ServiceA)的另外一个方法调过来的(以下简称方法A)。

         问4:哦,那个调用它的方法配了事务么(问题可能在这了)?

         答4:没有。

         问5:那WEB层的Action(用的是Struts2),调用的是没有声明事务的方法A,方法A再调用声明了事务的方法B?

         答5:对的。

         问6:你直接在方法A上加上事务声明看看

         答6:好。。。

         

         看来可能找到问题所在了,于是把@Transactional也加在方法A上,启动项目测试,结果是:事务正常生效,方法A和方法B都在一个事务里了。

         好了,现在总结一下现象:

         1、ServiceA类为Web层的Action服务

         2、Action调用了ServiceA的方法A,而方法A没有声明事务(原因是方法A本身比较耗时而又不需要事务)

         3、ServiceA的方法A调用了自己的方法B,而方法B声明了事务,但是方法B的事务声明在这种情况失效了。

         4、如果在方法A上也声明事务,则在Action调用方法A时,事务生效,而方法B则自动参与了这个事务。 

           我让他先把A也加上事务声明,决定回来自己再测一下。

         

           这个问题,表面上是事务声明失效的问题,实质上很可能是Spring的AOP机制实现角度的问题。我想到很久以前研究Spring的AOP实现时发现的一个现象:对于以Cglib方式增强的AOP目标类,会创建两个对象,一个事Bean实例本身,一个是Cglib增强代理对象,而不仅仅是只有后者。我曾经疑惑过这一点,但当时没有再仔细探究下去。

         

          我们知道,Spring的AOP实现方式有两种:1、Java代理方式;2、Cglib动态增强方式,这两种方式在Spring中是可以无缝自由切换的。Java代理方式的优点是不依赖第三方jar包,缺点是不能代理类,只能代理接口。

          Spring通过AopProxy接口,抽象了这两种实现,实现了一致的AOP方式:

    现在看来,这种抽象同样带了一个缺陷,那就是抹杀了Cglib能够直接创建普通类的增强子类的能力,Spring相当于把Cglib动态生成的子类,当普通的代理类了,这也是为什么会创建两个对象的原因。下图显示了Spring的AOP代理类的实际调用过程:

    因此,从上面的分析可以看出,methodB没有被AopProxy通知到,导致最终结果是:被Spring的AOP增强的类,在同一个类的内部方法调用时,其被调用方法上的增强通知将不起作用。

        

          而这种结果,会造成什么影响呢:

          1:内部调用时,被调用方法的事务声明将不起作用

          2:换句话说,你在某个方法上声明它需要事务的时候,如果这个类还有其他开发者,你将不能保证这个方法真的会在事务环境中

          3:再换句话说,Spring的事务传播策略在内部方法调用时将不起作用。不管你希望某个方法需要单独事务,是RequiresNew,还是要嵌套事务,要Nested,等等,统统不起作用。

          4:不仅仅是事务通知,所有你自己利用Spring实现的AOP通知,都会受到同样限制。。。。

    [解难]

         

          问题的原因已经找到,其实,我理想中的AOP实现,应该是下面这样:

    只要一个Cglib增强对象就好,对于Java代理方式,我的选择是毫不犹豫的抛弃。

          至于前面的事务问题,只要避开Spring目前的AOP实现上的限制,要么都声明要事务,要么分开成两个类,要么直接在方法里使用编程式事务,那么一切OK。

    原文地址:http://blog.csdn.net/seelye/article/details/40144817

  • 相关阅读:
    Linux常用命令
    Springboot环境搭建_第一个例子
    java 填写一个银行卡如何判断是否真实存在
    java 理解如何实现图片验证码 傻瓜都能看懂。
    编程语言学习路线··
    他他他她她她所唱所写………
    Docker 学习笔记 (4)
    Docker 学习笔记 (3)
    Docker 学习笔记 (2)
    Docker 学习笔记 (1)
  • 原文地址:https://www.cnblogs.com/youqc/p/8644925.html
Copyright © 2020-2023  润新知