transactionmanager
大家好,开篇先来谈谈Spring事务的优点吧,即spring事务的存在价值。首先它提供了非侵入式编码的事务实现,这个是通过aop实现的,具体的实现过程之前也写博客分析了。
另外,spring还提供了一套标准的事务管理工作流程。简单的说,事务管理一共可分为三个步骤,分别是初始化事务、提交事务、回滚事务,然后每个步骤又可细分为若干小步骤。spring事务工作流相当于为用户屏蔽了具体orm框架的底层处理逻辑,基于spring开发的程序,即便更换了orm框架,即便从本地事务切换到全局事务,也只需要简单的更改配置,选用合适的事务管理器,基本不会修改代码,这就是spring事务另一个优点。
Spring的PlatformtransactionManager是事务管理的顶层接口,其中定义的三个方法对应的就是上述的三个步骤,然后AbstractPlatformTransactionManager抽象类给出了三个步骤的具体实现。当然,AbstractPlatformTransactionManager也留下了几个未实现的抽象方法,具体有“创建事务对象”,“开始事务”,“执行提交”,“执行回滚”,这几个抽象方法的实现跟具体的orm框架(如:myBATis,hibernate)或事务特性(如:本地事务,全局事务)有关。比如今天要介绍的主角datasourceTransactionManager,它实现了上述的抽象方法,它适用的场景是“mybatis”,“ jdbctemplate”,"本地事务"。下面我们就具体分析DataSourceTransactionManager的事务管理三大步骤详细处理流程。
一. DataSourceTransactionManager初始化事务
1. 获取事务名称
默认事务名称是被代理类的全限定名称+当前被拦截的方法名称,如: testmybatis.TxDao.findList。
2. 获取事务属性对象(TransactionDefinition)
事务属性对象持有事务的相关配置,比如事务的隔离级别,传播行为,是否只读等。我们开启spring事务管理时,通常都会在配置文件里加入这样一段配置。
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
spring解析上述配置会创建两个事务属性对象。第一个事务属性对象适用于get前缀的方法对象,它是只读事务;第二个事务对象匹配非get前缀的其它方法,它使用默认事务配置,默认配置的话非只读,事务超时时间为-1,隔离级别使用数据库默认配置,传播行为是PROPAGATION_requireD。
3. 获取事务管理器
事务管理器跟事务属性对象一样,通常用户都已经配置好了,直接从spring的bean工厂获取,另外从配置也看出DataSourceTransactionManager持有dataSource。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="jdbcDataSource" />
</bean>
4. 创建事务对象
这个方法是AbstractPlatformTransactionManager定义的抽象方法,DataSourceTransactionManager实现了这个抽象方法,它创建的事务对象类型是DataSourceTransactionObject,事务管理器和事务对象之间是存在对应关系的。然后尝试从当前线程获取ConnectionHolder(连接持有器),赋值给事务对象。ConnectionHolder实现了ResourceHolder接口,它持有一个数据库连接,可以把凡是实现了ResourceHolder接口的对象看做是一种资源对象。默认情况下此时当前线程是不存在ConnectionHolder的,因此获取不到。插播一句,在spring事务管理过程中会用到一些线程安全对象,这些对象都交由TransactionSynchronizationManager管理,TransactionSynchronizationManager把这些对象都保存在Threadlocal中。
5. 获取被挂起资源持有器
被挂起资源持有器持有两类被挂起的资源,一类是资源对象,如上述的ConnectionHolder,一类是同步对象,即实现了TransactionSynchronization接口的对象。默认在这个时候事务同步还未被激活,因此返回null。
6. 创建事务状态对象
被创建的事务状态对象类型是DefaultTransactionStatus,它持有上述创建的事务对象。事务状态对象主要用于获取当前事务对象的状态,比如事务是否被标记了回滚,是否是一个新事务等等。
7. 开始事务
7.1. 由于此时事务对象DataSourceTransactionObject持有的ConnectionHolder为null,因此首先需要创建ConnectionHolder对象,创建ConnectionHolder对象的前置条件是要先获取数据库连接对象,于是从dataSource获取连接对象,把连接设置成手动提交,完成ConnectionHolder对象的创建,然后赋值给事务对象。另外这个时候上述的事务属性对象派上用处了,根据事务属性对象配置连接的相关属性,如隔离级别、超时时间等。
7.2. 把资源对象ConnectionHolder置入线程安全的map,key是dataSource对象,value是ConnectionHolder对象本身,当orm框架执行数据库操作需要连接对象时,获得就是这个ConnectionHolder持有的连接。
8. 预准备事务同步(主要是调用TransactionSynchronizationManager对象的方法,设置事务相关同步对象)
8.1. 设置当前线程事务激活为true。
8.2. 把事务隔离级别设置到当前线程。
8.3. 把事务是否只读设置到当前线程。
8.4. 把事务名称设置到当前线程。
8.5. 实例化当前线程的同步对象,默认是一个空集。
9. 创建事务信息对象(TransactionInfo)
事务信息对象持有“事务管理器”,“事务属性对象”,“事务状态对象”,可以把事务信息对象看做对以上几个对象的打包。然后把事务信息对象置入当前线程。
二. ORM框架(mybatis)执行数据库操作
1. 通过mybatis's Defaultsessionfactory获取defaultsqlSession。
2. 根据defaultSqlSession创建SqlSessionHolder(资源对象),把SqlSessionHolder置入当前线程,key是DefaultSessionFactory。
3. 创建SqlSessionSynchronization(同步对象),它持有的资源对象是SqlSessionHolder,把同步对象注册到当前线程。
4. 从当前线程获取ConnectionHolder对象,key是dataSource,从ConnectionHolder获取连接对象,
5. 执行数据库操作。
关于同步对象,资源对象和事务之间的关系请看下图:
三. DataSourceTransactionManager回滚事务
1. 判断是否有必要对抛出的异常执行回滚操作,默认只要异常对象派生自runtimeexception或ERROR,都会执行回滚操作。这个用户是可以配置的,比如这样。
<tx:attributes>
<tx:method name="*" rollback-for="com.cn.untils.exception.XyzException"/>
</tx:attributes>
2. 调用事务管理器的rollback(transactionStatus)方法,传入事务状态对象。
2.1. 触发同步对象的beforeCompletion方法。
2.2. 从事务对象获取连接持有器,然后再获取连接对象,调用连接的rollback()方法。
2.3. 触发同步对象的afterCompletion方法。
四. DataSourceTransactionManager提交事务
1. 调用事务管理器的rollback(transactionStatus)方法,传入事务状态对象。
1.1. 触发同步对象的beforeCommit方法。
1.2. 触发同步对象的beforeCompletion方法。
1.3. 从事务对象获取连接持有器,然后再获取连接对象,调用连接的commit()方法。
1.4. 触发同步对象的afterCommit方法。
1.5. 触发同步对象的afterCompletion方法。ro