`
zzc1684
  • 浏览: 1224408 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

小心,SpringAOP声明式事务的一个缺陷

 
阅读更多

[问题]

       Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring AOP啊,事务管理真轻松啊,真轻松;事务管理代码没有了,脑不酸了,手不痛了,一口气全配上了事务;轻量级,测试起来也简单,嘿!”。不管从哪个角度看,轻量级声明式事务都是一件解放生产力的大好事。所以,我们“一直用它”。

 

      不过,最近的一个项目里,却碰到了一个事务管理上的问题:有一个服务类,其一个声明了事务的方法,里面做了三次插入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。

分享到:
评论

相关推荐

    Hibernate编程式事务与Spring Aop的声明式事务(spring与hibernate集成)

    本篇文章将重点讨论如何在Spring框架中集成Hibernate,并探讨编程式事务管理和Spring AOP的声明式事务。 首先,让我们理解Spring与Hibernate的集成。为了整合这两个库,你需要在Spring配置文件中定义Hibernate的...

    spring AOP(声明式事务管理)小程序

    它们决定了一个事务能看到其他事务的哪些修改,选择合适的隔离级别可以避免脏读、不可重复读和幻读等问题。 6. **回滚规则**: 在Spring中,如果@Transactional注解的方法内抛出了未检查异常(继承自...

    使用Spring的声明式事务----AOP方式

    这段代码表示`UserService`类的所有方法都将在一个事务中执行,如果saveUser方法抛出异常,事务将会回滚。 接下来,我们来看看Spring是如何实现事务的。在Spring中,事务管理器(如DataSourceTransactionManager或...

    Spring ax/aop声明式事务配置实例

    在Spring框架中,AOP(面向切面编程)和声明式事务管理是两个核心特性,它们极大地简化了Java企业级应用中的事务处理。本实例将深入探讨如何在Spring中使用AOP来实现声明式事务配置。 一、Spring AOP基础 AOP允许...

    spring aop jar 包

    例如,可以使用`@Aspect`注解定义一个切面类,`@Before`、`@After`等注解来声明通知,`@Pointcut`定义切点表达式。 在实际开发中,Spring AOP广泛应用于事务管理。例如,我们可以定义一个切面来处理所有数据库操作...

    Spring AOP配置事务方法

    该元素用于定义一个事务特性,包括事务的传播行为、回滚规则等。在上面的配置文件中,我们可以看到 `&lt;tx:advice&gt;` 元素用于定义一个名为 "sgis.sdk.txAdvice" 的事务特性,该特性将所有以 "add*"、"delete*"、...

    Spring源代码解析(六):Spring声明式事务处理.doc

    Spring 声明式事务处理 Spring 中的事务处理可以分为两种方式:声明式事务处理和编程式事务处理。声明式事务处理通过 AOP 的实现,把事务管理代码作为方面封装到业务代码中,使得事务管理代码和业务代码解藕。这...

    简单spring aop 例子

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点问题,如日志、事务管理、安全性等。本示例将简要介绍如何在Spring应用中实现AOP,通过实际的...

    Spring AOP 16道面试题及答案.docx

    引介(Introduction)是Spring AOP的一个特性,允许在通知对象中声明并实现它们原本未实现的额外接口。通过`@DeclareParents`注解可以实现引介。 连接点(Joint Point)是程序执行过程中的特定点,通常在Spring AOP...

    Spring Aop四个依赖的Jar包

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的一个重要模块,它通过提供声明式的方式来实现面向切面编程,从而简化了应用程序的开发和维护。在Spring AOP中,我们无需深入到每个...

    Spring AOP完整例子

    在Spring AOP的例子中,我们可能会创建一个`@RunWith(SpringJUnit4ClassRunner.class)`标记的测试类,以利用Spring的测试支持。在测试方法中,可以注入需要的bean,然后调用方法来触发AOP代理。这样,通知将在适当的...

    spring aop 五个依赖jar

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点,如日志、事务管理等。在Java应用中,AOP通过代理模式实现了切面编程,使得我们可以将业务逻辑...

    java springAOP 事务+注释

    当应用于方法时,该方法将运行在一个事务中。若应用于类,整个类的所有公共方法都将具有相同的事务属性。注解的属性包括`value`(指定事务管理器的bean名)、`propagation`(事务传播行为)、`isolation`(隔离级别...

    spring AOP依赖三个jar包

    - **事务管理**:Spring AOP的一个常见应用就是声明式事务管理,通过`@Transactional`注解可以轻松地在方法级别控制事务。 综上所述,spring AOP依赖的这三个jar包构成了Spring框架面向切面编程的基础,它们共同...

    spring基于AOP实现事务

    如果不存在,则新建一个事务。 4. **异常回滚规则**:"-java.lang.Exception"表示如果方法抛出任何类型的Exception,事务都将回滚。如果没有异常,事务将在方法正常结束时提交。 通过这种方式,我们可以将事务管理...

    spring aop依赖jar包

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,它为Java应用程序提供了声明式的企业级服务,如事务管理、性能监控等。在Spring AOP中,我们可以通过定义切面(Aspect...

    springAOP 4个jar包

    在Spring AOP中,当无法使用Java的标准接口代理(例如,目标类没有接口)时,CGLIB就会被用到,生成一个目标类的子类并在其中插入切面逻辑。CGLIB-nodep版是精简版,不依赖于外部的ASM库,降低了依赖性。 4. **aop...

    spring声明式事务处理demo

    1. **配置Spring容器**:在Spring的配置文件(如`applicationContext.xml`)中,我们需要定义一个`&lt;tx:annotation-driven&gt;`元素,这将启用基于注解的声明式事务管理。同时,需要配置数据源(DataSource)和事务管理...

    Spring AOP实现机制

    - 当目标对象实现了至少一个接口时,Spring会使用JDK的java.lang.reflect.Proxy类创建一个代理对象。 - 代理对象在调用实际方法前后,会插入相应的通知代码,从而实现AOP功能。 - **CGLIB代理**: - 如果目标...

Global site tag (gtag.js) - Google Analytics