SpringBoot中的Transaction研究(三)TransactionInterceptor

概略

站在全局的角度来看如下图,TransactionInterceptor借助于AOP使用TransactionManager实现了事务,主要是完成了try-catch语法。至于具体的commitrollback的进一步了解参见SpringBoot中的Transaction研究(一)TransactionManager
在这里插入图片描述整个操作的核心代码是在TransactionAspectSupport里面(TransactionInterceptor继承了该类)

详细研究

作为整个事务管理的核心类,TransactionAspectSupport对事务的处理如下图
TransactionAspectSupportTransactionIntercepter.invoke在被调用后先是获取了targetClass,这样就有了添加在Method上面的annotation配置信息,然后通过调用父类的invokeWithinTransaction完成功能。
invokeWithinTransaction是核心方法,它针对三种情况分别处理,不过逻辑是类似的。抛开reactive的情况,另外两个是类似的,只是一个将操作现在进行,而另一个是将操作作为回调函数,对于前一种需要自己获取TransactionStatus,而后一种TransactionStatus会出现在回调的入参里。下面讨论这两种类似的情况中共同的逻辑。

PlatformTransactionManager的情况

prepareTransactionInfo

在transactionManager,txAttribute,TransactionStatus都就绪的情况下调用prepareTransactionInfo来获取TransactionInfo。另外还有一个参数是joinpointIdentification我看除了写日志外貌似没事用处。
这个方法执行了三个操作,首先创建了一个TransactionInfo,然后在有事务配置的情况下设置TransactionStatus,最后把当前线程的TransactionInfo设置为刚创建的。

TransactionInfo.bindToThread

这个方法以及后面会出现的restoreThreadLocalStatus两个方法需要额外介绍下,这里TransactionInfo通过链表的形式实现了一个栈的数据结构,bindToThread相当于压栈而restoreThreadLocalStatus相当于弹出。

completeTransactionAfterThrowing

这个方法主要是根据TransactionAttribute.rollbackOn来决定rollback还是commit当前的事务。
在这里插入图片描述上图列出来自带的四个实现类,其中Ejb3TransactionAttributeDelegatingTransactionAttribute两个都是代理类,前者把方法代理给了异常的annotation
DefaultTransactionAttribute是跟随EJB的处理方式,对于RuntimeException和Error进行回滚,而对于受检异常则不作处理,也就是要交给业务代码处理(毕竟Exception也是由业务代码处理)
RuleBasedTransactionAttribute对于回滚的判定代码非常复杂,如下

/**
	 * Winning rule is the shallowest rule (that is, the closest in the
	 * inheritance hierarchy to the exception). If no rule applies (-1),
	 * return false.
	 * @see TransactionAttribute#rollbackOn(java.lang.Throwable)
	 */
	@Override
	public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}

		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;

		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				int depth = rule.getDepth(ex);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}

		// User superclass behavior (rollback on unchecked) if no rule matches.
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			return super.rollbackOn(ex);
		}

		return !(winner instanceof NoRollbackRuleAttribute);
	}

这里假设有多个回滚规则(这些规则可以分为两类,一类是NoRollbackRuleAttribute,一类不是),找到rollbackRules中和当前异常最接近的规则,然后看该规则是回滚还是不回滚(找不到则使用super,也就是DefaultTransactionAttribute来处理)。而所谓的规则代码如下

/**
	 * Return the depth of the superclass matching.
	 * <p>{@code 0} means {@code ex} matches exactly. Returns
	 * {@code -1} if there is no match. Otherwise, returns depth with the
	 * lowest depth winning.
	 */
	public int getDepth(Throwable ex) {
		return getDepth(ex.getClass(), 0);
	}


	private int getDepth(Class<?> exceptionClass, int depth) {
		if (exceptionClass.getName().contains(this.exceptionName)) {
			// Found it!
			return depth;
		}
		// If we've gone as far as we can go and haven't found it...
		if (exceptionClass == Throwable.class) {
			return -1;
		}
		return getDepth(exceptionClass.getSuperclass(), depth + 1);
	}

可以看出来就是根据继承和名字匹配这两点来处理。从代码上来看,当我们配置回滚ExceptionA.class的时候也会把SomeExceptionA.class也回滚掉,即使这两个异常没有任何继承关系。看来给异常起名也要注意。如果想避免这个方法,就需要加上包名了。这样也提供了另一个种可能,就是把一个包里所有的异常都回滚掉。这就需要借助Transactional.rollbackForClassName而不是Transactional.rollbackFor
关于这里地方配置的由来就需要参见SpringBoot中的Transaction研究(四)Transactional

cleanupTransactionInfo

这个地方处理是前面TransactionInfo.bindToThread的反向操作,恢复了transactionInfoHolder之前保留的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值