那些年面试官问过我的 Spring 事务传播行为

序言

2018年的5月,我从深圳匆匆地来到了上海这座陌生的城市。上海很大,很快我就约到了几个面试,其中一家是某总部在杭州做二手车交易的公司。作为刚毕业一年的小菜鸡,我怀着忐忑的心情走进了这家公司在上海的办公地点,面积不大,大概只有不到300平米。当时会议室已经被占满,我坐在工位上答的笔试题。坐在我旁边的小伙伴好心的提醒了我:“Spring 事务传播行为你掌握的怎么样?等会很可能会问你这个。” 纳尼?心中一万个草泥马在奔跑。趁着面试官还没到,我用手机匆匆查了下,当时只记得有7种事务传播行为,但是临时抱佛脚,很快就忘了有具体哪些,后面的结果可想而知…

回去后我又查了一些资料,然而也只是从概念上对 “事务传播行为” 这个东西有一些了解。如今已时隔三年,我的技术能力也提高了很多,回想当初应该是对这个技术点理解的不够深入,因此记忆也不够深刻,恰逢最近在看 Spring 的源码,IOC 和 AOP 两大核心特性看完后便是对 AOP 的应用:事务。本篇将尝试从 Spring 事务传播行为出发,试图理解 Spring 事务管理的内部实现。

系列文章目录

前已经写了几篇文章作为本篇的铺垫,如果你对相关知识不熟悉可以参阅之前的文章。


  1. 《Java 基础知识之 JDBC》
  2. 《数据库事务基础知识》
  3. 《如何正确打开 Spring 事务?》

Spring 中的事务传播行为

事务管理并非 Spring 首创,Spring 也借鉴了很多其他的框架,然后加以统一。

在 Spring 中,我们经常使用声明式事务,在方法或类上添加 Spring 的 @Transtional 注解,在这个注解中我们可以指定事务传播行为,这个注解也参考了 EJB 的 javax.ejb.TransactionAttribute 以及 JTA 的 javax.transaction.Transactional,这里先通过对比认识一下这三者的异同。

参数\注解Spring @TransactionalEJB @TransactionAttributeJTA @Transactional
事务管理器transactionManager
传播行为propagationvaluevalue
隔离级别isolation
超时时间timeout
是否只读readOnly
回滚异常rollbackFor、rollbackForClassNamerollbackOn
不回滚异常noRollbackFor、noRollbackForClassNamedontRollbackOn

从上面的表格中可以看到,在 Spring 的 @Transactional 中都可以找到 EJB、JTA 注解中相应的参数。事实上,Spring 也对 EJB 的 @TransactionAttribute 注解及 JTA 的 @Transactional 加以了支持,在 Spring 中这三个注解都可以使用。

现在将重点转向事务传播行为,上面的三个注解都有事务传播行为,那么这三者的事务传播行为又有何异同呢?

传播行为\定义位置Spring PropagationEJB TransactionAttributeTypeJTA TxType
需要事务REQUIREDREQUIREDREQUIRED
支持事务SUPPORTSSUPPORTSSUPPORTS
强制事务MANDATORYMANDATORYMANDATORY
需要新事务REQUIRES_NEWREQUIRES_NEWREQUIRES_NEW
不支持事务NOT_SUPPORTEDNOT_SUPPORTEDNOT_SUPPORTED
从不使用事务NEVERNEVERNEVER
嵌套事务NESTED

有没有发现一些问题?Spring 中的事务定义与 EJB、JTA 基本一致,它们名称不仅相同,事实上语义和实现也相似,而且 Spring 还增加了一个 NESTED 类型的事务传播行为。

事务传播行为主要是控制新方法执行时是否使用事务,如何处理线程中以存在的事务。下面是对 Spring 中的这7中事务传播行为的描述。

  1. REQUIRED:默认的事务传播行为;需要事务:存在事务则使用已存在事务,否则创建新的事务;
  2. SUPPORTS:支持已存在事务:存在事务则使用已存在事务,否则以非事务方式运行;
  3. MANDATORY:强制使用事务:存在事务则使用已存在事务,否则抛出异常;
  4. REQUIRES_NEW:需要新事务:存在事务则挂起已存在事务,否则创建新事务;
  5. NOT_SUPPORTED:不支持事务:存在事务则挂起已存在事务,否则以非事务方式运行;
  6. NEVER:从不使用事务:存在事务则抛出异常,否则以非事务方式运行;
  7. NESTED:嵌套事务:存在事务则使创建保存点使用嵌套的事务,否则创建新的事务。

Spring 事务传播行为实现

Spring 事务管理的核心接口是 PlatformTransactionManager,这个接口提供了获取事务、提交事务、回滚事务的方法,子类 AbstractPlatformTransactionManager 提供模板方法,提供了事务管理的主要实现。事务传播行为用于控制事务的获取,因此查看事务获取的相关源码即可了解其实现。

public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {

	@Override
	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException {
			
		TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

		// 获取事务
		Object transaction = doGetTransaction();
		... 省略日志相关代码

		if (isExistingTransaction(transaction)) {
			// 事务是一个已存在的活动事务,根据事务隔离级别处理事务
			return handleExistingTransaction(def, transaction, debugEnabled);
		}
		... 省略校验相关代码
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			// 不存在事务,抛出异常
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		} else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			... 省略日志相关代码
			try {
				return startTransaction(def, transaction, debugEnabled, suspendedResources);
			} catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		} else {
			... 省略日志相关代码
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
		}
	}
}

这段代码先获取事务对象,然后根据事务是否为线程上下文中已存在的事务执行不同的动作。线程上下文中不存在事务的逻辑已经在上面的代码中体现,存在事务的逻辑也类似,与事务传播行为的定义保持一致。我这里画了一张图便于理解。
Spring 事务传播行为

总结

Spring 定义了不同的事务传播行为,用于指定获取 TransactionStatus 时的行为,到底使用线程上下文中已有的事务对象,还是创建新的事务对象,还是抛出异常。

Spring 获取的获取事务对象关联着线程上下文中保存的资源对象,对于 JDBC 来说就是 Connection,因此只要获取到 Spring 在线程上下文中保存的资源对象就可以接入 Spring 的事务管理,Spring JDBC、MyBatis 都是这个原理。后续会对 MyBatis 与 Spring 的整合进行分析。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鹏cool

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值