Spring源码分析十八:事务实现⑤ - 事务的提交

一、前言

本文是笔者阅读Spring源码的记录文章,由于本人技术水平有限,在文章中难免出现错误,如有发现,感谢各位指正。在阅读过程中也创建了一些衍生文章,衍生文章的意义是因为自己在看源码的过程中,部分知识点并不了解或者对某些知识点产生了兴趣,所以为了更好的阅读源码,所以开设了衍生篇的文章来更好的对这些知识点进行进一步的学习。

全集目录:Spring源码分析:全集整理


由于 事务的源码和 前篇的 Aop 源码逻辑很类似,所以本篇中某些内容不会展开去讲解,建议先阅读完 Spring源码分析十一:@AspectJ方式的AOP再来阅读本文会更好理解。


这是一个巨长的篇章…
全集目录如下:

  1. Spring源码分析十四:事务实现① - AutoProxyRegistrar
  2. Spring源码分析十五:事务实现② - ProxyTransactionManagementConfiguration
  3. Spring源码分析十六:事务实现③ - 事务的创建
  4. Spring源码分析十七:事务实现④ - 事务的回滚
  5. Spring源码分析十八:事务实现⑤ - 事务的提交

二、事务的提交 - commitTransactionAfterReturning

commitTransactionAfterReturning 的实现在 TransactionAspectSupport#commitTransactionAfterReturning 中。完成了事务的提交任务。

上面我们分析了Spring的事务异常处理机制。如果事务的执行并没有出现任何异常,那么也就意味着事务可以走正常的提交流程了。

	protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
    
    
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
    
    
			if (logger.isTraceEnabled()) {
    
    
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
			}
			// 进行事务提交
			txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
		}
	}

在真正的数据提交前,还需要做一个判断,在上面分析事务异常处理规则的时候,当某个事务既没有保存点也不是新事物的时候,Spring对他的处理方式只是设置一个回滚标识(在事务回滚的第三种回滚策略中完成 )。这个回滚标识在这里就会派上用场了。应用场景如下:
某个事务是另一个事物的嵌套事务,但是这些事务又不在Spring的管理范围内,或者无法设置保存点,那么Spring会通过设置回滚标识的方式来禁止提交。首先当某个嵌入事务发生回滚的时候会设置回滚标识,而等到外部事物提交时,一旦判断当前事务流被设置了回滚标识,则由外部事物统一来进行整体的事务回滚。
所以当事务没有被异常捕获的时候也并不意味着一定会执行提交流程。

我们来看看 commit 方法。commit方法的实现在 AbstractPlatformTransactionManager#commit

	@Override
	public final void commit(TransactionStatus status) throws TransactionException {
    
    
		if (status.isCompleted()) {
    
    
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		// 如果在事务链中已经被标记回滚,那么不会尝试提交事务,直接回滚。这种就是
		if (defStatus.isLocalRollbackOnly()) {
    
    
			if (defStatus.isDebug()) {
    
    
				logger.debug("Transactional code has requested rollback");
			}
			processRollback(defStatus, false);
			return;
		}

		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
    
    
			if (defStatus.isDebug()) {
    
    
				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			}
			processRollback(defStatus, true);
			return;
		}
		// 处理事务提交
		processCommit(defStatus);
	}

1. processRollback(defStatus, false);

这个方法是回滚方法,请移步观看回滚文章

2. processCommit(defStatus);

processCommit(defStatus); 的实现在AbstractPlatformTransactionManager#processCommit 中。在这个方法中完成了事务的提交操作。在提交过程中也并不是直接提交,而是考虑了诸多方面。符合提交条件的如下:

  1. 当事务状态中有保存点信息的话便不会去提交事务。
  2. 当事务非新事务的时候也不会去执行提交事务操作。
    此条件主要考虑内嵌事务的情况们对于内嵌事务,在Spring中正常的处理方式是将内嵌式无开始之前设置保存点,一旦内嵌事务出现啊异常便根据保存点信息进行回滚,但是如果没有出现异常,内嵌事务并不会单独提交,而是根据事务流最外层事务负责提交,所以如果当前存在保存点信息便不是最外层事务,不做保存操作,对于是否是新事物的判断也是基于此考虑。
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    
    
		try {
    
    
			boolean beforeCompletionInvoked = false;

			try {
    
    
				boolean unexpectedRollback = false;
				// 预留操作
				prepareForCommit(status);
				// 调用自定义触发器的方法
				triggerBeforeCommit(status);
				triggerBeforeCompletion(status);
				beforeCompletionInvoked = true;
				// 如果设置了保存点信息
				if (status.hasSavepoint()) {
    
    
					if (status.isDebug()) {
    
    
						logger.debug("Releasing transaction savepoint");
					}
					// 清除保存点信息
					unexpectedRollback = status.isGlobalRollbackOnly();
					status.releaseHeldSavepoint();
				}
				// 如果是新事物
				else if (status.isNewTransaction()) {
    
    
					if (status.isDebug()) {
    
    
						logger.debug("Initiating transaction commit");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
					// 如果是独立事务则直接提交
					doCommit(status);
				}
				// 不是新事物并不会直接提交,而是等最外围事务进行提交。
				else if (isFailEarlyOnGlobalRollbackOnly()) {
    
    
					unexpectedRollback = status.isGlobalRollbackOnly();
				}

				// Throw UnexpectedRollbackException if we have a global rollback-only
				// marker but still didn't get a corresponding exception from commit.
				if (unexpectedRollback) {
    
    
					throw new UnexpectedRollbackException(
							"Transaction silently rolled back because it has been marked as rollback-only");
				}
			}
			catch (UnexpectedRollbackException ex) {
    
    
				// can only be caused by doCommit
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
				throw ex;
			}
			catch (TransactionException ex) {
    
    
				// can only be caused by doCommit
				if (isRollbackOnCommitFailure()) {
    
    
					doRollbackOnCommitException(status, ex);
				}
				else {
    
    
					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				}
				throw ex;
			}
			catch (RuntimeException | Error ex) {
    
    
				if (!beforeCompletionInvoked) {
    
    
					triggerBeforeCompletion(status);
				}
				// 提交过程中会出现异常则回滚
				doRollbackOnCommitException(status, ex);
				throw ex;
			}

			// Trigger afterCommit callbacks, with an exception thrown there
			// propagated to callers but the transaction still considered as committed.
			try {
    
    
				triggerAfterCommit(status);
			}
			finally {
    
    
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
			}

		}
		finally {
    
    
			// 清理事务信息
			cleanupAfterCompletion(status);
		}
	}

DataSourceTransactionManager#doCommit 中的实现也就是至极调用数据库连接的 commit 方法。

protected void doCommit(DefaultTransactionStatus status) {
    
    
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
    
    
			logger.debug("Committing JDBC transaction on Connection [" + con + "]");
		}
		try {
    
    
			// 调用commit 方法
			con.commit();
		}
		catch (SQLException ex) {
    
    
			throw new TransactionSystemException("Could not commit JDBC transaction", ex);
		}
	}

三、总结

事务的提交并不是直接提交,也是有几个方面的考虑:

  1. 如果事务被设置了回滚标识,则不会回滚
  2. 如果事务中有保存点则不会回滚
  3. 如果事务非新事务的时候也不会去执行提交事务操作。

以上:内容部分参考
《Spring源码深度解析》
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

猜你喜欢

转载自blog.csdn.net/qq_36882793/article/details/107299336