

@Transactional Spring 事务源码分析
source link: https://mp.weixin.qq.com/s/PNnMCD68rg8xLfTkL8bo0w
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

@Transactional Spring事务源码分析
在Spring开发中,经常会用到这个注解,但它是怎么起到开启事务的作用的,还一知半解,只知道应该是基于AOP的,今天就来分析一波它的用法和具体实现。
编程式事务和声明式事务
Spring 支持两种事务管理方式:编程式和声明式,编程式是在程序中显式地使用TransactionTemplate来控制事务的开启和提交。
声明式事务是使用@Transactional注解,在类或方法上使用这个注解,就可以起到开启事务的作用,声明式事务是基于AOP的方式,在方法前开启一个事务,在方法执行后进行commit,中间进行一些异常的判断和处理。
相比来说,声明式事务使用起来更加优雅,AOP的方式对代码没有侵入性,比较推荐在日常开发中使用。
声明式事务的用法
下面看一下@Transactional注解的各项参数。
timtout
timtout是用来设置事务的超时时间,可以看到默认为-1,不会超时。
isolation
isolation属性是用来设置事务的隔离级别,数据库有四种隔离级别:读未提交、读已提交、可重复读、可串行化。MySQL的默认隔离级别是可重复读。
readOnly
readOnly属性用来设置该属性是否是只读事务,只读事务要从两方面来理解:它的功能是设置了只读事务后在整个事务的过程中,其他事务提交的内容对当前事务是不可见的。
那为什么要设置只读事务呢?它的好处是什么?可以看出它的作用是保持整个事务的数据一致性,如果事务中有多次查询,不会出现数据不一致的情况。所以在一个事务中如果有多次查询,可以启用只读事务,如果只有一次查询就无需只读事务了。
另外,使用了只读事务,数据库会提供一些优化。
但要注意的是,只读事务中只能有读操作,不能含有写操作,否则会报错。
propagation
propagation属性用来设置事务的传播行为,对传播行为的理解,可以参考如下场景,一个开启了事务的方法A,调用了另一个开启了事务的方法B,此时会出现什么情况?这就要看传播行为的设置了。
REQUIRES_NEW 和 NESTED非常容易混淆,因为它们都是开启了一个新的事务。我去查询了一下它们之间的区别,大概是这样:
REQUIRES_NEW是开启一个完全的全新事务,和当前事务没有任何关系,可以单独地失败、回滚、提交。并不依赖外部事务。在新事务执行过程中,老事务是挂起的。
NESTED也是开启新事务,但它开启的是基于当前事务的 子事务 ,如果失败的话单独回滚,但如果成功的话,并不会立即commit,而是等待外部事务的执行结果,外部事务commit时,子事务才会commit。
rollbackFor
在@Transactional注解中,有一个重要属性是roolbackFor,这是用来判断在什么异常下会进行回滚的,当方法内抛出指定的异常时,进行事务回滚。 rollbackForClassName 也是类似的。
rollbackFor有个问题是默认情况会做什么,以前认为默认会对所有异常进行回滚,但其实默认情况下只对RuntimeException回滚。
noRollbackFor
这个和上面正好相反,用来设置出现指定的异常时,不进行回滚。
声明式事务的实现机制
Spring在调用事务增强器的代理类时会首先执行TransactionInterceptor进行增强,在TransactionInterceptor的invoke方法中完成事务逻辑。首先看下TransactionInterceptor的类图结构。
TransactionInterceptor
在invoke方法里,调用了父类的模板方法invokeWithinTransaction,下面我们看下TransactionAspectSupport类。
createTransactionIfNecessary
可以看到,这个invokeWithinTransaction方法已经包含了事务执行的整个流程,这里是使用了模板模式,具体的实现交给子类去实现。下面我们分析一下其中的重要方法。
getTransaction
下面们看下用来用来获取TransactionStatus的getTransaction方法,这个方法是在AbstractPlatformTransactionManager抽象类中,
可以看到getTransaction是用来做了一些事务的初始化工作,包括一些判断,新建事务等等。
其中一些对已有事务的处理、嵌入式事务的处理的细节,暂时就略过了~
completeTransactionAfterThrowing
下面我们回到TransactionInterceptor,看一下下一个流程:回滚
上面有个rollbackOn(ex)方法,是用来判断回滚类型的,我们看下它的实现,它有不同的实现类,看下默认的
确实是RuntimeException和Error。
我们接着看一下回滚的实现。
processRollback
上面的代码中,有保存点的是指的嵌套事务,因为嵌套事务并不是真正的两个事务,所以会有保存点的信息进行回滚。
事务的提交和回滚的流程类似,同样进行了不同事务类型的判断,在此不进行额外的分析。
结语
通过idea的反编译工具,分析了一波代码,同时参考了《Spring源码解析》,练习了一下分析源码的能力,能大体地看出事务的实现过程,大概看过之后感觉Spring这样写是非常合理的,代码非常清晰,每个功能点都拆分地特别细。博客没有分析事务的加载,因为那块和事务本身的处理并没有太大的关系,而是Spring对注解等属性的加载并生成TransactionInterceptor代理对象。
而且Spring里面使用了非常多的模板方法,使用模板方法对我们的好处是,能从大到小、从整体到局部地了解到整个过程,而且大大降低代码的耦合性。以后在编码时也会注意多使用模板方法。
文章同步发表于 https://blog.csdn.net/acingdreamer/article/details/91873745
Recommend
-
17
系列文章索引: 一. Spring事务分类 Spring 提供了两种事务管理方式:声明式事务管理和编程式事务管理。 1.1编程式事务 在 Spring 出现以前,编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中显式调用 begi...
-
13
系列文章索引: 系列文章索引: 什么是cglib Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。JDK必须强制基于interface接口类型:...
-
22
系列文章索引: 什么是代理 理设计模式提供了对目标对象的间接访问方式,能力模式能够解耦合并且便于扩展目标的功能。 在现实生活这,我们消费者如果要去购买一杯牛奶的时候,并不是直接去找牛奶厂商购买,而是在便利店或者超市购买(...
-
7
@Transactional注解解决事务处理问题 作者: wencst 分类: JAVA,Uncategorized 发布时...
-
8
[Spring声明式事务使用的那些坑] 最全的@Transactional注解的避坑指南哈喽小伙伴们,俺是啤酒熊,今天想来和大家聊聊Spring中涉及数据库事务的一些坑。Spring
-
7
每个Java程序员都犯过的Spring事务@Transactional错误 - Kozhenkov可能最常用的 Spring 注释之一是@Transactional。尽管它很受欢迎,但它有时会被误用,从而导致一些不是软件工程师想要的东西。在这篇文章中,我收集了我个人在项目中遇到的问题。我希望...
-
5
Transactional失效场景介绍 第一种 Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。例如以下代码。 定义一个错误的@Transactional标注实现,修饰一个默认访问符的方法 /** * @author zhoujy...
-
2
聊聊Spring事务控制策略以及@Transactional失效问题避坑-51CTO.COM
-
6
Spring异步Async和事务Transactional注解 Spring开发...
-
6
目前JAVA的微服务项目基本都是SSM结构(即:springCloud +springMVC+Mybatis),而其中Mybatis事务的管理也是交由spring来管理,大部份都是使用声明式事务(@Transactional)来进行事务一致性的管理,然后在实际日常开发过程中,发现很多开发同学都用错了spring声明...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK