spring事务相关源码分析

目录

前言

二、创建事务

1.总体流程

2.获取事务

二、事务回滚

三、提交事务

 四、添加事务提交之后的回调方法

总结



前言

       spring的事务和mybatis有什么关系,如何实现的提交和回滚,隔离级别和传播机制如何发挥作用,这一切的答案都将在本文揭晓,本文主要集中与事务相关的运行流程,关于容器启动过程中的事务相关配置过程没有涉及,以后有时间再说吧。


一、事务执行的总体流程

       事务的整体流程表面上看比较简单,首先是创建事务,运行业务代码,当有需要回滚的异常发生时,回滚当前事务,没有异常发生,就提交当前事务,当然,当使用了setRollbackOnly()方法时也会回滚,这个问题再具体分析,接下来我们对每个重点方法进行分析。当事务因为异常回滚之后会再次将异常抛出,外层事务也会因为异常回滚。

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
			throws Throwable {

		// If the transaction attribute is null, the method is non-transactional.
		final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			//创建事务
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

			Object retVal;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				//执行业务方法
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
				//当系统发生异常时,拦截器会手动抛出异常,能够被外层事务捕获,造成外层事务回滚,那么doSetRollbackOnly(status)
				//的意义何在,此处应是为了兼容手动事务回滚,TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
				//手动事务回滚能够在开启新事务的情况下,只会回滚当前事务;在共用旧事务的情况下,会记录回滚标志,在外层方法提交时回滚。
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
			//没有异常发生,提交事务
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}
	}




二、创建事务




1.总体流程

       创建事务的过程中有两个方法比较重要,分别实现了获取事务和将transactionInfo对象写入本地线程中,接下来分别进行分析。

	protected TransactionInfo createTransactionIfNecessary(
			PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {

		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
				//获取事务
				status = tm.getTransaction(txAttr);
			}
		}
		//准备transactionInfo对象,并写入本地线程中
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

2.获取事务

       这里需要关注一下dobegin和prepareSynchronization两个方法,关于事务传播机制相关的代码比较复杂,会在下一篇文章中进行分析。

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
		Object transaction = doGetTransaction();
		/**
		 * 1、这里是new一个新的DataSourceTransactionObject对象
		 * 在后面开启事务 doBegin()方法中,会对txObject对象进行填充
		 */
		// Cache debug flag to avoid repeated checks.
		boolean debugEnabled = logger.isDebugEnabled();
	    //事务传播机制相关代码,下一篇进行分析
		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}

		// Check definition settings for new transaction.
		if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
			}
			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				//开启事务
				doBegin(transaction, definition);
				//Activate transaction synchronization,会影响mybatis对sqlsession的获取
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException ex) {
				resume(null, suspendedResources);
				throw ex;
			}
			catch (Error err) {
				resume(null, suspendedResources);
				throw err;
			}
		}
	}

        doBegin方法实现了数据库连接的获取,并将连接写入到本地线程中,同时将连接的隔离级别设置为注解中的隔离级别,事务相关操作大部分都是通过连接实现的。

	protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
				//使用数据库连接池获取连接
				Connection newCon = this.dataSource.getConnection();
				if (logger.isDebugEnabled()) {
					logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
				}
				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}

			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			con = txObject.getConnectionHolder().getConnection();
			//将注解中的隔离级别写入到connection中,从而对数据库查询起作用
			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
			txObject.setPreviousIsolationLevel(previousIsolationLevel);

			// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
			// so we don't want to do it unnecessarily (for example if we've explicitly
			// configured the connection pool to set it already).
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				//设置手动提交
				con.setAutoCommit(false);
			}

			prepareTransactionalConnection(con, definition);
			txObject.getConnectionHolder().setTransactionActive(true);

			int timeout = determineTimeout(definition);
			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
				txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
			}

			// Bind the connection holder to the thread.
			//将数据库连接写入本地线程,mybatis获取连接时从本地获取,从而保证连接一致
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
			}
		}

	}

二、事务回滚

       数据库连在事务回滚的时候分了三种情况,当前事务有停留点时,回滚到停留点;当前事务是新事务时,直接回滚;当前事务是子事务,设置回滚标志。

	private void processRollback(DefaultTransactionStatus status) {
		try {
			try {
				//如果有停留点,则回滚到停留点
				triggerBeforeCompletion(status);
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Rolling back transaction to savepoint");
					}
					status.rollbackToHeldSavepoint();
				}
				//如果是新事务,直接回滚
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction rollback");
					}
					doRollback(status);
				}
				//如果和外层方法共用一个事务,设置回滚标志,在外层回滚
				else if (status.hasTransaction()) {
					if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
						if (status.isDebug()) {
							logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
						}
						doSetRollbackOnly(status);
					}
					else {
						if (status.isDebug()) {
							logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
						}
					}
				}
				else {
					logger.debug("Should roll back transaction but cannot - no transaction available");
				}
			}
		finally {
			cleanupAfterCompletion(status);
		}
	}

       事务回滚的真正执行方法是调用connention的回滚方法,所以需要保证同一个事务中的数据库相关操作使用同一个connection,下一篇文章中对此进行分析。

三、提交事务

        即使程序过程中没有异常,当设置了事务回滚的时候也会造成回滚,但这种情况下的回滚是不会抛出异常的,也就是内层设置事务回滚不会导致外层事务回滚。事务提交的真正执行方法也是调用connection的提交方法,关键还是保证同一个事务的数据库操作同一个connection。

	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);
			return;
		}
        //根据connectionHolder的属性判断是否回滚
		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);
	}

        即使当内外层共用同一个事务的时候,如果内层事务回滚,但是没有抛出异常,也就是没有让外层事务感知,即设置了事务持有的ConnectionHolder的rollbackOnly属性为true,由于内外层共用连接,此时外层事务也要回滚,但是会抛异常。

      在源码中表现为isGlobalRollbackOnly,即判断当前连接是否需要回滚。

	public boolean isGlobalRollbackOnly() {
		return ((this.transaction instanceof SmartTransactionObject) &&
				((SmartTransactionObject) this.transaction).isRollbackOnly());
	}

    public boolean isRollbackOnly() {
	    return getConnectionHolder().isRollbackOnly();
	}

        同一个事务如果是由与连接的rollbackOnly属性导致事务回滚,会在调用processRollback方法方法中传入属性true,最终导致抛出UnexpectedRollbackException异常。

			//当事务持有的连接标记为回滚。却没有发生异常时
			if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
Exception in thread "main" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

 四、添加事务提交之后的回调方法

        本文在事务方法提交之后,可以异步调用一些方法。在事务管理类中有一个synchronizations属性,是线程本地类型的,能够为当前事务添加一些异步方法。

public abstract class TransactionSynchronizationManager {

	private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
			new NamedThreadLocal<>("Transaction synchronizations");
}

       在事务提交的processCommit方法中,有triggerAfterCommit方法,在这个方法中会调用注册的异步回调方法。

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
		try {
			boolean beforeCompletionInvoked = false;
			// 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);
		}
	}

	public static void triggerAfterCommit() {
		invokeAfterCommit(TransactionSynchronizationManager.getSynchronizations());
	}

	public static void invokeAfterCommit(@Nullable List<TransactionSynchronization> synchronizations) {
		if (synchronizations != null) {
			for (TransactionSynchronization synchronization : synchronizations) {
				synchronization.afterCommit();
			}
		}
	}

public void insert(TechBook techBook){
        bookMapper.insert(techBook);
       // send after tx commit but is async
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                System.out.println("send email after transaction commit...");
            }
        }
       );
    }



总结

        本文主要对spring中事物的基本流程进行了分析,但还留下了几个问题,spring和mybatis是如何保证connection一致的,事务的传播机制,以及将事物的traninfo信息写入本地线程中的作用吗 ,将在接下来一一解析。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值