博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring事务管理的一些注意点
阅读量:5981 次
发布时间:2019-06-20

本文共 4212 字,大约阅读时间需要 14 分钟。

总结一些自己最近在使用spring事务管理时碰到的一些注意点

 

一、关于目标对象内部方法自我调用时的一些情形和存在的问题

1、情形1:只给b方法上加事务注解,a方法上不加

目标类的接口和实现代码示例:

public interface AService {    public void a();    public void b();} @Service()public class AServiceImpl implements AService{    public void a() {        this.b();    }    @Transactional(rollbackFor={Exception.class})       public void b() {         insert();         update();    }}

 

 

只要给目标类AServiceImpl的某个方法加上注解@Transactional,spring就会为目标类生成对应的代理类,以后调用AServiceImpl中的所有方法都会先走代理类(即使调用未加事务注解的方法a,也会走代理类),即在通过getBean("AServiceImpl")获得的业务类时,实际上得到的是一个代理类,假设这个类叫做AServiceImplProxy ,spring为AServiceImpl生成的代理类类似于如下代码:

public class AServiceImplProxy implements AService{    public void a() {      //反射调用目标类的a方法    }    public void b() {          //启动事务的代码       //反射调用目标类的b方法      //事务提交的代码    }}

 

 

 

由于目标类中只有b方法加入了事务管理,所以代理类中只为b方法加入了横切事务逻辑,spring事务管理的本质是通过aop为目标类生成动态代理类,并在需要进行事务管理的方法中加入事务管理的横切逻辑代码(如AServiceImplProxy中的b方法所示)。

调用getBean("AServiceImpl").a()时,实际上执行的是AServiceImplProxy.a(),代理类的a方法会通过反射调用目标类的a方法, 再在目标类的a方法中调用b方法,故最终a中调用的b方法是来自于AServiceImpl中的b方法,AServiceImpl的b方法并没有横切事务逻辑代码(切记:事务逻辑代码在代理类中,@Transactional只是标记此方法在代理类中要加入事务逻辑代码)。所以调用a方法时,b方法的事务会失效。

 

其实,在proxy对象与目标对象之间还有一个InvocationHandler对象(以jdk动态代理为例),真正的横切逻辑是放到InvocationHandler对象中的,调用逻辑分离到InvocationHandler中主要是为了构造出具有通用性和简单性的代理类,此处为了简化处理过程,统一放到代理对象中来说明,动态代理简化的调用关系图如下:

aop中存在方法嵌套调用时,相应的调用过程序列图如下:

 

2、情形2:给a方法加事务注解,b方法上加或不加

对1中的代码做修改,为a方法也加上事务注解:

 

@Service()public class AServiceImpl implements AService{   @Transactional(rollbackFor={Exception.class})     public void a() {        this.b();    }  @Transactional(rollbackFor={Exception.class})    public void b() {         insert();         update();    }}

 

此时生成的代理类类似如下代码:

 

public class AServiceImplProxy implements AService{    public void a() {     //启动事务的代码          //反射调用目标类的a方法          //事务提交的代码    }    public void b() {          //启动事务的代码      //反射调用目标类的b方法      //事务提交的代码      }}

 

即为a和b都加入了事务横切逻辑。在这种情况下,调用顺序还和1中情形类似,区别在于在反射调用目标对象的a方法前,会对a方法开启事务管理,虽然调用的b方法还是目标对象中没有加事务逻辑的代码,spring却会把b合并到a的事务中去,此时相当于只有一个事务。

如果再将目标类代码改为:

 

@Service()public class AServiceImpl implements AService{    @Transactional(rollbackFor={Exception.class})       public void a() {       this.b();    }       public void b() {       insert();       update();    }}

 

即只在a上加事务控制,由于b会合并到a的事务中,所以b中的逻辑也可以被事务管理。

 

由于a和b都合并到了a的事务中,所以这种情形下事务传递规则不适用。代理类中加了事务逻辑的b方法永远不会被调用。

 

那么问题来了,如果我想让b也执行自己的事务逻辑,即调用b时执行代理类中b方法的事务逻辑,该怎么办?

 

修改目标类中的a方法:

 

@Transactional(rollbackFor={Exception.class})public void a() {    ((AService) AopContext.currentProxy()).b();    //即调用AOP代理对象的b方法即可执行事务切面进行事务增强}

 

这时,就会强制要求调用代理类中的b方法,从而开启b上的事务,此时b事务上标注的事务传递规则也就可以生效了,详情参见:http://jinnianshilongnian.iteye.com/blog/1487235

个人觉得这种方法不太好,会污染业务逻辑代码,使代码变复杂。

 

还有一种办法就是接口下沉,把b方法分离到另一个接口中,从根源上避免目标对象内部方法自我调用。

 

二、try catch的问题

有时需要在业务逻辑代码中显式try catch包裹事务代码,以便在出现异常时进行一些别的处理。

目标类的接口和实现示例代码如下:

 

public interface AService {      public void a();} @Service()public class AServiceImpl implements AService{    @Transactional(rollbackFor={Exception.class})    public void a() {        try{                 insert();             update();             }catch(Exception e){             }    }}

 

自己在代码中显式捕获异常会导致spring事务回滚失效,原因:spring事务是通过aop捕获到异常后再执行回滚,如果业务代码中显式捕获了异常,会导致spring捕获不到,回滚自然失败。

有如下几种解决办法:

(1)业务代码catch住异常后重新抛出,如:

 

public void a() throws Exception{        try{                   insert();               update();            }catch(Exception e){                   throw new Exception(e);            }    }

 

不足是本方法的调用端也必须显式捕获异常。

(2)使用编程式事务显式回滚:

 

public void a() {        try{                   insert();                   update();             }catch(Exception e){                    //显式回滚                   TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();             }    }

 

不足是事务控制代码会侵入业务代码,也正是因为编程式事务管理会侵入业务逻辑代码,所以才有了申明式事务管理。

(3)接口下沉,将需要事务控制的代码分到另一个接口方法中,如:

 

public interface BService {    public void a();    public void b();} @Service()public class BServiceImpl implements BService{    @Transactional(rollbackFor={Exception.class})    public void b() {         insert();        update();    }}

 

相应的调用端a方法中变为:

public void a() throws Exception{        try{                      bService.b();             }catch(Exception e){                      throw new Exception(e);       }    }

 

 

转载地址:http://xhlox.baihongyu.com/

你可能感兴趣的文章
栈内存的销毁
查看>>
移动端网页布局适配rem方案小结
查看>>
大兄dei,早点看清this吧
查看>>
我竟然意外的懂了什么是“缓冲区”
查看>>
谈谈TCP协议的三次握手和四次挥手
查看>>
idea中live-template的使用
查看>>
理解区块链 这一篇就够
查看>>
Mac配置MySQL以及Squel Pro
查看>>
ES6入门---let和const
查看>>
遗传算法组卷使用心得
查看>>
linux入门学习
查看>>
一文带你了解求职面试那些名词(干货)
查看>>
海康,商汤,顺丰等研发面经
查看>>
周期性机遇下,脱离OPPO的Realme有何新打法?
查看>>
那些年命途多舛的千千静听,归来能否再现当年荣光?
查看>>
Scrapy框架的使用之Scrapy对接Splash
查看>>
ECMAScript Decorators---装饰器
查看>>
Android组件化专题 - 组件化配置
查看>>
Kotlin 之高阶函数与Lambda表达式与闭包
查看>>
【填坑日记】<transition-group> children must be keyed: <ElTag>
查看>>