总结一些自己最近在使用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);}}

spring事务管理的一些注意点相关推荐

  1. Spring事务管理 与 SpringAOP

    1,Spring事务的核心接口 Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略.  ...

  2. 什么是事务的传播_这么漂亮的Spring事务管理详解,你不来看看?

    事务概念回顾 什么是事务? 事务是逻辑上的一组操作,要么都执行,要么都不执行. 事物的特性(ACID): 原子性: 事务是最小的执行单位,不允许分割.事务的原子性确保动作要么全部完成,要么完全不起作用 ...

  3. SPRING 事务管理说明

    spring 事务管理是通过AOP拦截指定的方法,进行事务管理. 事务配置 <aop:config proxy-target-class="true"><aop: ...

  4. Spring 事务管理高级应用难点剖析

    Spring 事务管理高级应用难点剖析: 第 1 部分 http://www.ibm.com/developerworks/cn/java/j-lo-spring-ts1/index.html htt ...

  5. spring事务管理 TransactionProxyFactoryBean源码分析

    J2EE,当然离不开事务,事务又当然少不了Spring声明式事务.spring声明式事务,很多码农门,应该和笔者一样,停留在使用上,及仅仅了解点原理.如:Spring事务管理原理"代理+AO ...

  6. Spring 事务管理高级应用难点剖析--转

    第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN& ...

  7. Spring事务管理(详解+实例)

    写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: ...

  8. 在Spring事务管理下,Synchronized为啥还线程不安全?

    在synchronized 锁住方法的情况下,竟然出现了脏写 Tips 昨天本来打算是准备着一支烟 一杯咖啡 一个bug写一天的,突然我们组长跟我们说线上环境报错了, 还出现了"服务器异常, ...

  9. 多数据源 事务管理_可能是最漂亮的Spring事务管理详解

    事务概念回顾 什么是事务? 事务是逻辑上的一组操作,要么都执行,要么都不执行. 事物的特性(ACID): 原子性: 事务是最小的执行单位,不允许分割.事务的原子性确保动作要么全部完成,要么完全不起作用 ...

  10. springaop事务逻辑原理_架构师:一篇文章掌握——Spring 事务管理

    对大多数Java开发者来说,Spring事务管理是Spring应用中最常用的功能,使用也比较简单.本文主要逐步介绍Spring事务管理的相关知识点及原理,作为Spring事务管理的学习总结. 一.关键 ...

最新文章

  1. Maven2整合集成IntelliJ IDEA创建Web项目
  2. 服务器系统由32位的吗,云服务器有Windows32位系统吗
  3. MATLAB之相关函数
  4. Charles之过滤信息
  5. 使用C语言进行面向对象的开发--GObject入门[2]
  6. java泛型怎么用反射生成_Java 之 使用反射生成并操作对象
  7. android oreo 开机动画,Android O添加了一个 Splash Screen API帮助简化启动屏制作
  8. 计算机专业410分能上哪些大学,2021年高考410分能报什么学校
  9. 迭代终止准则的三种形式_一种经验模态分解筛选迭代过程终止准则的方法与流程...
  10. Zookeeper概念学习系列之zookeeper是什么?
  11. Windows TensorFlow环境搭建
  12. 判断小写字母 php,php中怎样推断字母是大写照样小写_后端开发
  13. Linq 三表 left join 的实现
  14. 写题10分钟,写题解8小时,一道头条面试题,真心难。
  15. uni-app 开发微信小程序,网络断开,无网络情况,刷新检查,重新加载
  16. 视频直播app源码,保存和提取图片
  17. Sql Server 的基本增删改查语句
  18. C语言嵌入式系统编程修炼之键盘操作
  19. linux用迅雷下载软件,wine 迅雷下载安装(wine 迅雷极速版)
  20. PVID和VID详解

热门文章

  1. 锁屏界面提示某些设置已隐藏_OPPO忘记锁屏密码怎么办?教你一招轻松解开!...
  2. HttpClient工具类 HttpClientUtils.java
  3. Spring Boot Mybatis简单使用
  4. java 弹幕游戏_JAVA 弹幕小游戏 1.0版本
  5. jni android rect.h,解决 fatal error: jni_md.h: No such file or directory #include “jni_md.h”
  6. 2020 r7000 龙腾屏幕_R7000和R7000P到底有什么差别,差多少?
  7. Abbott's Revenge UVA - 816 (输出bfs路径)
  8. java学到什么程度安卓_自学Android到什么程度才能找到工作?
  9. php pdo总结,php 总结(10) PDO 连接数据库 预处理
  10. flex 左右布局_移动端开发常用布局:前端弹性布局总结