美文网首页Java服务器端编程程序员Spring Boot
分析动态代理给Spring事务埋下的坑

分析动态代理给Spring事务埋下的坑

作者: 张丰哲 | 来源:发表于2017-11-04 19:59 被阅读1751次

    前言

    Spring的声明式事务让我们不在编写获得连接、关闭连接、开启事务、提交事务、回滚事务等代码,通过一个简单的@Transactional注解,就让我们轻松进行事务处理。我们知道Spring事务基于AOP,采用动态代理实现,虽然使用简单,但是在实际场景中,我们也会遇到一些坑。而往往遇到坑之后,我们都会茫然,这是由于没有对Spring事务的实现机制做一点了解导致的。因此本篇博客将从原理的角度分析下动态代理给Spring事务埋下的坑!

    从动态代理到Spring事务

    UserService:

    UserService接口

    txMethod和txMethod2方法模拟事务方法(相当于@Transactional)

    noTxMethod方法是普通方法

    UserServiceImpl:

    UserServiceImpl

    在Spring事务中,我们往往是在Service层进行事务控制。

    我们在UserServiceImpl中想模拟的是:

    一个有事务的方法,去调用另一个有事务的方法,会怎么样?

    一个没有事务的方法,去调用一个有事务的方法,会怎么样?

    UserHandler:

    UserHandler

    这里为了简便,通过方法名称来判断是否开启事务。

    显然,txMethod方法、txMethod2方法都“应该”开启事务。

    UserTest:

    UserTest

    下面,我们来说下运行结果:

    proxyInstance.txMethod2()方法,会开启事务,这没有问题。

    proxyInstance.txMethod()方法,虽然在事务方法txMethod()内部调用了txMethod2()事务方法,但是并没有新开启事务。

    proxyInstance.noTxMethod()方法,虽然在没有事务的方法noTxMethod()内部调用了有事务的txMethod2()方法,但是并没有开启事务。

    下面让我们来对应下Spring事务中的现象:

    Spring事务

    上述的情况,说白了,就是在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务!

    为什么会这样呢?

    其实通过上面的动态代理的代码,你应该可以发现:

    动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!

    那么如何解决呢?

    很简单,我们完全可以在抽出一个XxxService,在其内部调用UserService.txMethod()和UserService.txMethod2()方法即可。总而言之,避免在一个Service内部进行事务方法的嵌套调用!(因为动态代理导致这种场景事务失效了。)

    好像Spring事务如此简单,但是背后却有这些道道,你被坑过么?

    have a good weekend~

    2017.11.04 zhangfengzhe

    相关文章

      网友评论

      • maxwellyue:指定使用AspectJ这种动态代理方式,是不是也能解决问题?
        张丰哲:嗯嗯,同学,可以写篇博客分享下嘛,😄
      • 沐流尘:事务传播有几种类型哦,楼主别误导了别人
        张丰哲:博主从实际场景出发,文章哪里误导呢?可以具体指出么?:blush:
      • 半亩荒地:动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!这句话不好理解。更简单来说是:触发的是代理类实例的方法,而非原对象的方法
        张丰哲:为你点赞,:smiley:
      • 飘颜:这边文章没有从深度上说明,spring事物传播有好多种,你貌似只说了一种。:smile:
        张丰哲:恩恩,本篇文章剖析的是实际开发的场景可能遇到的问题,确实没有全面分析spring事务传播,你可以给我们分享下你的心得么,:smiley:
      • 莫那一鲁道:事物的传播机制能否解决这个问题呢
        张丰哲:事务的传播机制其实也是通过动态代理来完成,而文章就是分析的是动态代理产生的坑,:smile:
      • 尼古拉斯拖延蟹:可以通过在方法内部获得代理对象的方式,通过代理对象调用同类的其他方法,这也是Spring的官方文档中给出的方案。
        朋克小新:@星海辰光 beanFactory.getBean(name);重新取一次当前类的实例就好了
        5c2f033be3dd:@尼古拉斯拖延蟹
        请问上述情况如何在方法内部获取代理对象呀,获取谁的代理对象呢?刚入门的小白请教,您能否贴出代码让我学习学习呀?
        张丰哲:谢谢分享~“方法内部获得代理对象”这确实是一种解决方式~
      • 淡看江湖等雨:这种类似的还有异步方法,在同一个service中调用自己的异步方法
        张丰哲:恩,异步方法,谢谢分享~
      • e9a44a336dfe:你真正需要做的是,把自己注入到自己里面,问题就可以解决了。没必要专门为了这个情况,额外写多的文件,反而影响了接口的聚合。
        张丰哲:谢谢分享观点。不过自己注入自己(感觉非常奇怪?),平时工作中也没有看到过在有这么用的。博文中分享的也是实际中较为常用的解决方式吧。

      本文标题:分析动态代理给Spring事务埋下的坑

      本文链接:https://www.haomeiwen.com/subject/eixqmxtx.html