升职加薪的必备良品之玩转Spring事务源码

今日内容:

1. 什么是数据库的事务?

2. 为什么说Spring的事务就是数据库的事务?

3. 实现Spring事务的核心源码详解 

4. Spring事务传播属性详解

5. 实现Spring事务的传播属性

6. 如何从Spring源码到并发编程再到JVM ?

什么是数据库的事务?

数据库事务,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足ACID,即原子性、一致性、隔离性和持久性属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

为什么说Spring的事务就是数据库的事务?

本质上其实是同一个概念,spring的事务是对数据库的事务的封装,最后本质的实现还是在数据库,假如数据库不支持事务的话,spring的事务是没有作用的.数据库的事务说简单就只有开启,回滚和关闭,spring对数据库事务的包装,原理就是拿一个数据连接,根据spring的事务配置,操作这个数据连接对数据库进行事务开启,回滚或关闭操作.但是spring除了实现这些,还配合spring的传播行为对事务进行了更广泛的管理.其实这里还有个重要的点,那就是事务中涉及的隔离级别,以及spring如何对数据库的隔离级别进行封装.事务与隔离级别放在一起理解会更好些.

实现Spring事务的核心源码详解

一、事务配置

TransactionManagementConfigurationSelector.selectImports()负责定义外部加入spring容器的配置类

此方法最终在ConfigurationClassParser中被解析并最终实例化为bean

AutoProxyRegistrar.registerBeanDefinitions()把InfrastructureAdvisorAutoProxyCreator注册beandefinition

ProxyTransactionManagementConfiguration.transactionAdvisor()注入事务切面

二、事务创建

实际运行入口之一(还有cglib):JdkDynamicAopProxy..invoke()

ReflectiveMethodInvocation.proceed()切面调用链处理核心方法

TransactionInterceptor.invoke()从这里触发事务拦截

TransactionAspectSupport.invokeWithinTransaction()实现Spring事务的核心业务

TransactionAspectSupport.determineTransactionManager()定义指定的事务管理器

对于事务管理器,默认使用DataSourceTransactionManager(可配置数据源的事务管理器),也可自定义事务管理器,然后配置数据源即可。

createTransactionIfNecessary()创建事务信息TransactionInfo:其中包括数据源、事务连接(ConnectionHolder)、事务状态、连接缓存等;

DataSourceTransactionManager.doGetTransaction()获取事务对象:里面包含连接包装和缓存

TransactionSynchronizationManager.getResource()连接线程级缓存:确保当前线程拿到唯一的数据连接

AbstractPlatformTransactionManager.startTransaction()开启一个新事务

只有newTransaction为true时,spring才会做实际的提交或回滚,这里是一个很重要的点。很多嵌套事务的坑,都是这里没有理解清楚。

DataSourceTransactionManager.doBegin()开启事务核心源码

TransactionSynchronizationManager.bindResource()设置当前连接和数据源的线程级绑定

三、事务回滚

对于非编程式事务而言,Spring事务的核心实现其实就是用try / catch 包裹事务提交和回滚的范式而已,但提交和回滚里面的包装大有讲究。

TransactionAspectSupport.completeTransactionAfterThrowing()事务回滚实现

以上截图中doSetRollbackOnly(),不会在连接上实际设置回滚点,只打个标记(当前事务需要回滚, commit时会使用该标记)!

四、事务提交

AbstractPlatformTransactionManager.commit()事务实际提交源码这里

cleanupAfterCompletion()回收连接或恢复事务,这个点耐人寻味:当事务传播属性为Require_New时,会暂时挂起之前的连接,然后创建新连接;当新连接提交或回滚后,通过这个方法恢复之前连接和状态!!!!

Spring事务传播属性详解

事务传播行为是Spring框架独有的事务增强特性。

1.什么是事务传播行为??

事务传播行为是用来描述由某一个事务传播行为嵌套进另一个方法时的事务如何传播。

用伪代码说明

1.Required(默认属性):

如果存在一个事务,则支持当前事务。如果没有事务,则开启一个新的事务。

2.Supports

支持当前事务,如果当前没有事务,就以非事务的方式执行

3.MANDATORY

使用当前的事务,如果没有事务,就抛出异常

4.Requires_New

新建事务,如果当前存在事务,就把当前事务挂起。

5.Not_Supported

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6.Never

以非事务方式执行操作,如果当前存在事务,则抛出异常。

7.Nested

支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。

嵌套事务一个非常重要的概念就是内层事务依赖于外层事务,外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

实现Spring事务的传播属性

代码验证

假如说我们数据库里面有两张表,user1 和user2, 列名id 和 name

连接数据库可以用mybatis或者jdbc

然后,Dao层代码:

User1Mapper:

User2Mapper:

验证就在Service层实现

我们为User1Service和User2Service相应方法加上Propagation.REQUIRED属性。

User1Service方法:

User2Service方法:

然后我们来验证:

第一种,张三,李四都会插入进去,外围方法未开启事务,插入张三 李四方法在自己的事务中独立运行,外围方法异常不影响内部。

第二种,张三插入,李四未插入。外围方法没有事务,插入“张三”、“李四”方法都在自己的事务中独立运行,所以插入“李四”方法抛出异常只会回滚插入“李四”方法,插入“张三”方法不受影响。

结论:通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

如何从Spring源码到并发编程再到JVM ?

Java并发编程实战.pdf

深入理解Java虚拟机:JVM高级特性与最佳实践.pdf

Spring源码深度解析.pdf

三本好书,值得拥有

获取这三本书可以进群免费领取,群内提供免费的Java架构学习资料,QQ群:680075317,备注“木子”即可。

你可能感兴趣的:(升职加薪的必备良品之玩转Spring事务源码)