关于使用Spring声明式事务时,在类的内部方法互相调用时,Spring无法拦截内部方法调用,导致事务不起作用的问题研究

 一、问题现象:

          在程序设计中,有时候我们会遇到一种情况,在一个类的内部进行自我方法调用,然后通过调用这个方法使该

法进入事务管理,如图:

   

          Spring声明式事务管理的配置如图:

     

          接下来,当我们执行程序后发现控制台打印如下日志:

   

          现在,我们已经知道了本文所描述的问题出现所在,先不解答问题如何处理,我们先了解一下Spring声明式事

务的原理,其实Spring是基于它的AOP动态代理技术来实现声明式事务管理的,那么Spring的AOP动态代理又是怎么

样的呢?接下来我们会做介绍。

二、SpringAop动态代理:

       AOP 代理其实是由 AOP 框架动态生成的一个对象,该对象可作为目标对象使用。AOP 代理包含了目标对象的

全部方法,但AOP 代理中的方法与目标对象的方法存在差异:AOP 方法在特定切入点添加了增强处理,并回调了目

标对象的方法。

AOP 代理所包含的方法与目标对象的方法示意图如图所示:

         AOP 代理的方法与目标对象的方法

          


          Spring 的 AOP 代理由Spring的IoC 容器负责生成、管理,其依赖关系也由IoC 容器负责管理。因此,AOP

 代理可以直接使用容器中的其他 Bean 实例作为目标,这种关系可由 IoC 容器的依赖注入提供。

          纵观 AOP 编程,其中需要程序员参与的只有 3 个部分:

          1.定义普通业务组件。

          2.定义切入点,一个切入点可能横切多个业务组件。

          3.定义增强处理,增强处理就是在 AOP 框架为普通业务组件织入的处理动作。

          上面 3 个部分的第一个部分是最平常不过的事情,无须额外说明。那么进行 AOP 编程的关键就是定义切入

和定义增强处理。一旦定义了合适的切入点和增强处理,AOP 框架将会自动生成 AOP 代理,而 AOP 代理的方法大

致有如下公式:

           代理对象的方法 = 增强处理 + 被代理对象的方法

           在上面这个业务定义中,不难发现 Spring AOP 的实现原理其实很简单:AOP 框架负责动态地生成 AOP 代

类,这个代理类的方法则由 Advice 和回调目标对象的方法所组成。

           对于前面提到的图 2 所示的软件调用结构:当方法 1、方法 2、方法 3 ……都需要去调用某个具有“横

切”性质的方法时,传统的做法是程序员去手动修改方法 1、方法 2、方法 3 ……、通过代码来调用这个具有“横

切”性质的方法,但这种做法的可扩展性不好,因为每次都要改代码。

           于是 AOP 框架出现了,AOP 框架则可以“动态的”生成一个新的代理类,而这个代理类所包含的方法 1、

方法2、方法 3 ……也增加了调用这个具有“横切”性质的方法——但这种调用由 AOP 框架自动生成的代理类来负

责,因此具有了极好的扩展性。程序员无需手动修改方法 1、方法 2、方法 3 的代码,程序员只要定义切入点即可

—— AOP框架所生成的 AOP 代理类中包含了新的方法 1、访法 2、方法 3,而 AOP 框架会根据切入点来决定是否要

在方法1、方法 2、方法 3中回调具有“横切”性质的方法。

           简而言之:AOP 原理的奥妙就在于动态地生成了代理类。

           接下来,来看下我们的业务中,如果要使用AOP,那么过程应该是这样的,如下图所示:

   

           

            但是,实际上我们的代码是这样执行的,如下图:

   

            这样的结果就是,我们先前提到的最初的问题,调用insertValue()方法后,SpringAop拦截不到方法调用,造

成无法进入事务管理。

三、解决方案1:

       此处的解决方案是参照Spring官方对于自身方法调用的解决方法,利用暴露本地线程,代理目标类,来完成自

身调用时,触发AOP拦截,进入事务管理,核心工具类为,AopContext.currentProxy(),从AOP容器中取出代理类,在

内部中重新调用要拦截的方法,从而进入事务管理。

          Spring配置如下图:

   

           

           代码的写法如下图所示:

   

           

           此时进行数据库操作,控制台打印日志如下:

   

           当整个入库操作执行完成后,会提交事务,如下图:

   

            至此,事务提交完成,此方法可行。

        但是,这种方法有一个缺点,具有严重的代码侵入性,导致声明式事务的技术形同虚设,并且如果想取消事

,注释了Spring配置中的AOP相关配置,会出现非受检异常,导致程序无法运行。

            那么,如何避免具有代码侵入性呢?现在,我们使用解决方案2。


四、解决方案2:

        此处的解决方案是利用Spring的IOC容器,从IOC容器中重新获得代理类,调用业务类的insertValue()方法,

而使之进入事务管理,Spring配置如下图:

   

           代码写法如下图:

   

  

            要在类中注入Spring的应用程序上下文,

   

             

             此时,执行程序,并且观察控制台日志,如下图:

 

             至此,此解决方案完成。

             此方案的优点就是没有严重的代码侵入,并且,当你注释掉SpringAOP配置的时候,运行程序正常,只是不

入事务管理了而已,没有任何其他问题。

             唯一的缺点就是,要求程序设计者必须很清楚,哪里该进入事务,哪里就该从SpringIOC容器中取到此对

象的实例,并调用要进入事务的方法。

             最后总结一下就是,程序设计是一门危险的平衡艺术




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值