Spring Dao编程进阶-Spring中的事务控制模型

从 0 开始深入学习 Spring 专栏目录总览

前面的事务基础中,咱学习了如何使用编程式事务、声明式事务来控制业务层的事务,也知道这里面会有一个事务管理器,但也仅仅是知道。为了搞明白 SpringFramework 中定义的事务控制模型,咱这一章就来好好地分析一下,SpringFramework 当初是怎么设计的这套事务控制的模型抽象,以及这里面涉及到的一些其它的点。

1. Spring事务的三大核心【熟悉】

SpringFramework 的事务控制模型,实际上是三个最顶层的接口:

  • PlatformTransactionManager :平台事务管理器
  • TransactionDefinition :事务定义
  • TransactionStatus :事务状态

是不是突然产生了一点想法:简单的说,SpringFramework 对于事务的控制,可以理解为事务管理器,可以根据事务的定义,获取 / 控制事务的状态

下面咱分别解释这三个顶级接口对应的设计。

1.1 PlatformTransactionManager

平台事务管理器,其实可能有的小伙伴会疑惑,为什么要加上 Platform 的字眼呢?是这样,SpringFramework 一开始对于事务的控制,没有局限在单体应用的数据源上,它有设计基于 Hibernate 的、JPA 的、JTA (分布式事务)的,这些不同的类型,SpringFramework 把它视为不同的 “平台” ,所以才有了 “平台事务管理器” 一说。

这个 API 在咱前面的学习中已经屡次出现过了,SpringFramework 做事务控制,必须依赖事务管理器,所以这个 PlatformTransactionManager 在 Spring 事务控制界的地位至高无上。接下来咱还会单独开一个小节看看事务管理器的设计。

这里多说一嘴哈,其实在 SpringFramework 5.2 之后,PlatformTransactionManager 不再是顶级接口了,它有一个父接口叫 TransactionManager :

public interface PlatformTransactionManager extends TransactionManager

public interface TransactionManager {

}

然而 TransactionManager 中没有任何常量和方法的定义。。。那它图个啥呢?哎,文档注释给出了答案:( SpringFramework 的 javadoc 是真的好)

Marker interface for Spring transaction manager implementations, either traditional or reactive.

它仅仅是传统平台事务管理器,或者响应式事务管理器的标识接口而已。

得了,它的作用有点类似 Serializable ,都是作为一个标识而已。这个东西小伙伴们知道一下就好,SpringFramework 在 2019 年底推出了一个 R2DBC 的工程,它可以实现响应式 JDBC ,那响应式的 JDBC 自然就要用到响应式的事务管理器咯,所以这里它就把原来的 PlatformTransactionManager 做了进一层的父级接口。

1.2 TransactionDefinition

事务定义,也或者叫做事务明细 / 事务属性。类比于 Bean 的 BeanDefinition ,想必小伙伴们理解它一定不难,这里面肯定存放了好多好多有关事务的属性,不用我说你也能列出几个来:

  • 事务隔离级别
  • 事务传播行为
  • 是否为读写事务
  • 超时时间
  • 。。。。。。

是不是在 @Transactional 注解 / <tx:method> 标签中的那些可以定义的属性,最终都会封装到这个 TransactionDefinition 中呀!哎它就是这么回事,所以这个抽象在学了 BeanDefinition 之后反而更好理解了。

1.3 TransactionStatus

事务状态,这里面记录的是当前事务运行的状态,比方说:

  • 是否是一个全新的事务
  • 是否有保存点
  • 事务是否完成
  • 。。。。。。

貌似跟 TransactionDefinition 有点相似之处哈,注意 status 是实例的概念,definition 是定义的概念,有点像是 bean 和 BeanDefinition 的概念对比哈。所以小伙伴就应该知道,TransactionStatus 是一个事务一个状态,TransactionDefinition 是针对某个事务的配置,封装的定义信息。

不过有一点要知道,这个 TransactionStatus 我们在平时开发中接触不到(前面也没碰到过),它是 SpringFramework 内部用来控制事务的封装,咱后面在分析原理时能看到。

它跟 PlatformTransactionManager 一样,也是在 SpringFramework 5.2 之后,TransactionStatus 不再是顶级接口,它抽象了一个父接口叫 TransactionExecution ,作为传统 JDBC 的 TransactionStatus 与 R2DBC 的 ReactiveTransaction 的公共父接口。

2. 事务管理器的设计【了解】

既然上面咱分析了,事务管理器很重要,那 SpringFramework 一定对这部分有很完善的实现。下面咱看看 PlatformTransactionManager 都设计了什么。

2.1 PlatformTransactionManager的接口方法定义

public interface PlatformTransactionManager extends TransactionManager {
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;
	void commit(TransactionStatus status) throws TransactionException;
	void rollback(TransactionStatus status) throws TransactionException;
}

三个方法分别的含义:

  • getTransaction :传入 TransactionDefinition ,返回 TransactionStatus ,很明显它是根据一个事务的定义信息,查询事务当前的状态。
  • commit :提交事务,它需要传入事务当前的状态,来判断当前事务是否允许提交(如果当前事务已标记为需要回滚,则无法提交)
  • rollback :回滚事务,它也需要传入事务当前的状态,以此判断当前事务是否允许回滚(如果当前事务已经完成了,则无法回滚)

可见它的这个思路还是非常清晰的:前面咱在学习事务传播行为时,知道每个事务都有 name ,那 TransactionDefinition 中也应该有(或者类似于) name 的东西,根据这个获取到的 TransactionDefinition ,去 PlatformTransactionManager 中查询事务当前的状态,并可以依据此状态来决定事务的最终提交或回滚。PlatformTransactionManager 定义的这个接口,串起来的思路,本身就是 SpringFramework 事务控制的核心思路,只不过里面的实现细节和具体动作,那都是实现类干的事了。

2.2 PlatformTransactionManager的层次体系

借助 IDEA ,可以查看 PlatformTransactionManager 的上下级层次关系(为了展示 ORM 框架的事务管理器,这里额外引入了 spring-orm 的依赖):

Spring Dao编程进阶-Spring中的事务控制模型

由此可以提取出几个比较关键的 API ,咱重点看一下。

2.3 ResourceTransactionManager

从接口名上看,它应该是基于资源的事务管理器,可资源是什么呢?像之前在 IOC 部分讲的那个资源吗?很明显不是,这里的资源我们可以简单的理解为数据库(当然严格意义上不是,因为事务管理器还有基于消息中间件的)。那既然是基于资源,那肯定可以通过事务管理器来获取资源的源吧(有点绕),所以 ResourceTransactionManager 接口只定义了一个方法:

public interface ResourceTransactionManager extends PlatformTransactionManager {
	Object getResourceFactory();
}

由此就可以获取到资源来源了。观察上面的类层次关系图,可以发现 DataSourceTransactionManager 、HibernateTransactionManager 等都实现了这个接口,那它们能获取到的资源工厂,无非就是 DataSource (可以由 DataSource 获取到 Connection )与 SessionFactory(可以由 SessionFactory 获取到 Session )咯。咱可以简单地看看刚才提到的两个实现类时怎么实现的这个方法:

public Object getResourceFactory() {
    return obtainDataSource();
}

protected DataSource obtainDataSource() {
    DataSource dataSource = getDataSource();
    Assert.state(dataSource != null, "No DataSource set");
    return dataSource;
}
public Object getResourceFactory() {
    return obtainSessionFactory();
}

protected final SessionFactory obtainSessionFactory() {
    SessionFactory sessionFactory = getSessionFactory();
    Assert.state(sessionFactory != null, "No SessionFactory set");
    return sessionFactory;
}

跟刚才的推理得出来的结论一样吧,所以说实现了这个 ResourceTransactionManager 接口之后,就可以通过这个接口拿到被事务控制的源。

2.4 AbstractPlatformTransactionManager

这个抽象类,从原理的层面上讲,它是最值得研究的类之一了。它的内部对 PlatformTransactionManager 的三个核心方法进行了实现,并定义了大量的模板方法,留给各个实现类去实现,所以这个类小册不打算在这里展开讲,而是放到后面的事务原理部分再展开讲解。

2.5 DataSourceTransactionManager

这是最常用的事务管理器了,只要是用 jdbc 或者 MyBatis 作为持久层框架,咱都是配置它作为事务管理器的实现。它内部就组合了一个 DataSource :

public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
		implements ResourceTransactionManager, InitializingBean {
	private DataSource dataSource;

它的内部实现中,也不乏可以找到咱熟悉的字眼:

con.setAutoCommit(false); // 开启事务
con.commit(); // 提交事务
con.rollback(); // 回滚事务

它的内部实现,咱同样放到事务原理中再研究,这里小伙伴们有个印象即可。

其余的几个 HibernateTransactionManager 、JpaTransactionManager 等等,考虑到部分小伙伴没有接触过,再加上 Hibernate 在当下的大环境中本来用的就不多了,所以小册就不再拿出来讲解啦,感兴趣或者有需要的小伙伴可以自行跟进源码中看看研究一下。

3. 事务定义与事务状态【熟悉】

最后咱看看其余的两个核心接口,一个是 TransactionDefinition ,一个是 TransactionStatus,它们有些像 BeanDefinition 与 bean 对象实例,但又不太一样,小伙伴不要直接类比了。

3.1 事务定义

单轮 TransactionDefinition 这个接口,其实它可以讲的东西真的不多,因为咱已经学过声明式事务的配置方式了,那里面的属性就是对应的 TransactionDefinition 。不过话又说回来,TransactionDefinition 终究是个接口,到最后还得有实现类支撑,这个实现类可有点讲究,咱来研究一下它们吧。

3.1.1 TransactionDefinition的层次体系

同样的,借助 IDEA ,可以形成 TransactionDefinition 与其扩展的层次关系:

Spring Dao编程进阶-Spring中的事务控制模型

是不是发现这里面有几个还蛮新鲜 / 意外的?下面咱来逐个解读。

3.1.2 DefaultTransactionDefinition

这里面已经包含了上面提到的那些事务的属性了,本身没什么好说的。

public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {
	private int propagationBehavior = PROPAGATION_REQUIRED;
	private int isolationLevel = ISOLATION_DEFAULT;
	private int timeout = TIMEOUT_DEFAULT;
	private boolean readOnly = false;
	@Nullable
	private String name;
    // ......

3.1.3 TransactionAttribute

这是一个扩展自 TransactionDefinition 的接口,它里面额外定义了两个方法:

public interface TransactionAttribute extends TransactionDefinition {
    String getQualifier();
    boolean rollbackOn(Throwable ex);
}

这里比较重要的方法是下面的 rollbackOn 方法,它会判断事务定义中遇到指定的异常是否通知事务管理器回滚,说白了,它就相当于 @Transactional 注解中 rollbackFor 属性的底层支撑。

之前在第 57 章中,我们有说默认情况下,异常的回滚只能是 RuntimeException ,或者是 Error 的子类,这个是有底层可循的。翻开 TransactionAttribute 的基本实现类 DefaultTransactionAttribute ,可以发现 rollbackOn 方法的默认实现就是捕捉 RuntimeException 和 Error :

// DefaultTransactionAttribute
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

多说一嘴,其实在事务核心 AOP 拦截器的父类 TransactionAspectSupport 中,它获取事务定义时,就是直接拿的 TransactionAttribute 而不是 TransactionDefinition ,为的就是可以决定捕获哪些异常后回滚事务(后面到事务原理中会讲的)。

3.1.4 TransactionTemplate

没想到吧,它竟然也是 TransactionDefinition !可能小伙伴们会产生疑惑,这家伙咋还多余继承个 TransactionDefinition 呢?这个问题吧,小册帮你推理一下,看看是不是在理。

TransactionTemplate 的构建,需要传入事务管理器,由事务管理器来控制事务的提交和回滚,我们在使用 TransactionTemplate 的时候只关心内部的业务流程。那事务管理器在执行业务代码之前,肯定要先获取 TransactionTemplate 对应的事务的状态吧,那用什么获取呢?很明显,TransactionTemplate 本身就是一个事务的定义信息,根据它就可以从事务管理器中获取到这个 TransactionTemplate 对应的 TransactionStatus ,有了 TransactionStatus 才能完成接下来的检查、提交、回滚。

3.2 事务状态

说是 TransactionStatus 是三大核心之一,然而 TransactionStatus 并不是顶级接口。咱还是首先来看看 TransactionStatus 的类层次结构吧。

3.2.1 TransactionStatus的层次体系

借助 IDEA ,可以生成 TransactionStatus 为中心的主要类结构体系。

Spring Dao编程进阶-Spring中的事务控制模型

自上而下,咱逐个简单了解一下。

3.2.2 TransactionExecution

这个接口是从 SpringFramework 5.2 之后才有的,之所以这样做,也是跟上面的 TransactionManager 一样,为了再抽一套响应式的事务控制。这个接口里定义了 4 个本应该属于 TransactionStatus 的属性:

public interface TransactionExecution {
	boolean isNewTransaction(); // 是否是全新的事务
	void setRollbackOnly(); // 设置事务状态为需要回滚
	boolean isRollbackOnly(); // 事务是否需要回滚
	boolean isCompleted(); // 事务是否已完成
}

这 4 个方法可以说,无论是同步阻塞式事务,还是响应式事务,都是必需的属性了。

3.2.3 SavepointManager

保存点的管理器,说明 TransactionStatus 可以添加、回滚事务到指定的保存点上,形成嵌套事务。它里面定义了 3 个方法,刚好对应了保存点的 3 个操作的动作:

public interface SavepointManager {
	Object createSavepoint() throws TransactionException; // 创建保存点
	void rollbackToSavepoint(Object savepoint) throws TransactionException; // 回滚至指定的保存点
	void releaseSavepoint(Object savepoint) throws TransactionException; // 销毁保存点
}

不过话又说回来,保存点需要数据库的支持才行,不然咱搁这瞎操作一番也没啥用,,,

3.2.4 DefaultTransactionStatus

之所以把 AbstractTransactionStatus 跳过去,是因为它确实没什么好看的在,咱还是着重看落地的实现类吧。

DefaultTransactionStatus 是事务底层主要使用的 TransactionStatus 的实现类了,SpringFramework 的事务控制中大多都是用 DefaultTransactionStatus 记录事务状态信息。

与 DefaultTransactionStatus 平级的还有一个 SimpleTransactionStatus ,它在底层没有任何地方用到过,javadoc 中也说明了它的作用:

It is mainly provided as a start for custom transaction manager implementations and as a static mock for testing transactional code (either as part of a mock PlatformTransactionManager or as argument passed into a TransactionCallback to be tested).

它主要是作为自定义事务管理器实现的开始以及作为测试事务代码的静态模拟(作为模拟 PlatformTransactionManager 的一部分,或作为传递给要测试的 TransactionCallback 的参数提供)的。

得了,人家是给测试用的,那咱也甭看了。知道默认的实现是 DefaultTransactionStatus 就 OK 啦。

免责声明:
1.本站所有内容由本站原创、网络转载、消息撰写、网友投稿等几部分组成。
2.本站原创文字内容若未经特别声明,则遵循协议CC3.0共享协议,转载请务必注明原文链接。
3.本站部分来源于网络转载的文章信息是出于传递更多信息之目的,不意味着赞同其观点。
4.本站所有源码与软件均为原作者提供,仅供学习和研究使用。
5.如您对本网站的相关版权有任何异议,或者认为侵犯了您的合法权益,请及时通知我们处理。
火焰兔 » Spring Dao编程进阶-Spring中的事务控制模型