Spring事务二

尽管上面分析过了spring事务,但是感觉还是不够。那么来分析一下具体的信息。这里将会更加深入代码层面来研读这里的信息。

也会搭配spring集合mybatis中的事务来进行说明。

TransactionSynchronizationManager

首先看下TransactionSynchronizationManager,这个类在源码中有大量的应用。重点代码分布在两个区域

doBegin和prepareSynchronization方法中。

看下doBegin,这里是创建数据库连接、开启事务的地方

			// Bind the connection holder to the thread.
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}

如果是新创建的数据库连接,那么代码执行到这里的时候,就会将当前的数据库连接存入到ThreadLocal中来,看下具体的逻辑:

	public static void bindResource(Object key, Object value) throws IllegalStateException {
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
		Assert.notNull(value, "Value must not be null");
		Map<Object, Object> map = resources.get();
		// set ThreadLocal Map if none found
		if (map == null) {
			map = new HashMap<>();
			resources.set(map);
		}
		Object oldValue = map.put(actualKey, value);
		// Transparently suppress a ResourceHolder that was marked as void...
		if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
			oldValue = null;
		}
		if (oldValue != null) {
			throw new IllegalStateException(
					"Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");
		}
	}

看下对应的resources的底层数据结构

private static final ThreadLocal<Map<Object, Object>> resources =
      new NamedThreadLocal<>("Transactional resources");

这里的map中的key当前存的是datasource,对应的value是ConnectionHolder;那么也就是说将数据源和当前的数据库连接保存到当前线程中来。

但是这里只有开启了事务的情况下,那么在外部可以通过TransactionSynchronizationManager来获取得到这里的datasource和数据库连接。

总之TransactionSynchronizationManager可以获取得到事务的很多信息。

那么接着看下prepareSynchronization

protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
   if (status.isNewSynchronization()) {
      TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
      TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
            definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
                  definition.getIsolationLevel() : null);
      TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
      TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
      TransactionSynchronizationManager.initSynchronization();
   }
}

将当前事务的激活状态、隔离级别、是否只读、事务名称、事务的同步调用器保存在了当前线程中来。

在加了@Transactional事务的方法中可以使用TransactionSynchronizationManager来获取得到对应的信息。

这里来放入对应的测试代码:

@Service
public class UserService2 {

    @Autowired
    private JdbcTemplate jdbcTemplate;


    @Autowired
    private UserService2 userService;


    @Transactional
    public void inert() {
        jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void suspend() {
                String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
                System.out.println("当前事务被挂起了,执行这里的代码,当前事务的名称是:"+currentTransactionName);
            }
        });
        userService.update();
    }

    /**
     * newTransaction
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void update() {
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                                                                      @Override
                                                                      public void beforeCompletion() {
                                                                          String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
                                                                          System.out.println("当前事务回滚之前需要进行的操作"+currentTransactionName);
                                                                      }
                                                                  });
        jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
        throw new NullPointerException();
    }

}

在inset方法中,在事务被挂起时执行对应的代码,在update方法中,在事务回滚之前打印这里的代码。

打印事务名称相同

所以在这里首先看下一个有趣的现象:

@Service
public class UserService1 {

    @Autowired
    private JdbcTemplate jdbcTemplate;


    @Autowired
    private UserService1 userService;


    @Transactional
    public void inert() {
        System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
        jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
        userService.update();
    }


    @Transactional
    public void update() {
        System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
        jdbcTemplate.update("insert into account (id,name,money) values (?,?,?)", null, "lisi", 2000f);
        // throw new NullPointerException();
    }

}

打印的两个方法的事务名称是一样的!!按照上面的逻辑来说,不同的事务名称不应该是一样的吗。

重点在于update方法在创建事务的时候:prepareSynchronization

	protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
		if (status.isNewSynchronization()) {
			TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
					definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
							definition.getIsolationLevel() : null);
			TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
			TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
			TransactionSynchronizationManager.initSynchronization();
		}
	}

这里的if判断是不成立的。那么为什么这里的status.isNewSynchronization()为FALSE呢,那么看下status是怎么来进行构建的。

		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);

对于第一行代码来说,是为true的。但是对于构造中的代码:

	protected DefaultTransactionStatus newTransactionStatus(
			TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
			boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {
		// 这里因为当前事务正在活跃中,所以最终为FALSE
		boolean actualNewSynchronization = newSynchronization &&
				!TransactionSynchronizationManager.isSynchronizationActive();
		return new DefaultTransactionStatus(
				transaction, newTransaction, actualNewSynchronization,
				definition.isReadOnly(), debug, suspendedResources);
	}

suspendedResources

表示的挂起的资源,其实本质上就是数据库连接。

看下代码使用的地方:AbstractPlatformTransactionManager.getTransaction

SuspendedResourcesHolder suspendedResources = suspend(null);

继续进去:

	protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
         // 如果再次之前没有事务,那么这里肯定为FALSE;如果之前开启过事务,那么这里为true。
		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
			try {
				Object suspendedResources = null;
				if (transaction != null) {
					suspendedResources = doSuspend(transaction);
				}
				String name = TransactionSynchronizationManager.getCurrentTransactionName();
				TransactionSynchronizationManager.setCurrentTransactionName(null);
				boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
				TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
				Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
				TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
				boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
				TransactionSynchronizationManager.setActualTransactionActive(false);
				return new SuspendedResourcesHolder(
						suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
			}
			catch (RuntimeException | Error ex) {
				// doSuspend failed - original transaction is still active...
				doResumeSynchronization(suspendedSynchronizations);
				throw ex;
			}
		}
		else if (transaction != null) {
			// Transaction active but no synchronization active.
			Object suspendedResources = doSuspend(transaction);
			return new SuspendedResourcesHolder(suspendedResources);
		}
		else {
			// Neither transaction nor synchronization active.
			return null;
		}
	}

如果为FALSE,那么这里什么都不做返回null。

如果为true,那么这里在做什么?

private List<TransactionSynchronization> doSuspendSynchronization() {
   List<TransactionSynchronization> suspendedSynchronizations =
         TransactionSynchronizationManager.getSynchronizations();
   for (TransactionSynchronization synchronization : suspendedSynchronizations) {
      synchronization.suspend();
   }
   TransactionSynchronizationManager.clearSynchronization();
   return suspendedSynchronizations;
}

首先将当前线程中的资源挂起,然后清空;

protected Object doSuspend(Object transaction) {
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
   txObject.setConnectionHolder(null);
   return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}

将当前数据库连接置为空,然后在当前线程中解除绑定,这里返回的就是数据库连接的包装类。

然后获取得到当前事务的信息保存到了SuspendedResourcesHolder对象中来,而SuspendedResourcesHolder又会被存到DefaultTransactionStatus中

然后调用prepareTransactionInfo方法,存入到本地线程中去:

	protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, String joinpointIdentification,
			@Nullable TransactionStatus status) {

		TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
		if (txAttr != null) {
			// We need a transaction for this method...
			// The transaction manager will flag an error if an incompatible tx already exists.
			txInfo.newTransactionStatus(status);
		}
		.......
		// We always bind the TransactionInfo to the thread, even if we didn't create
		// a new transaction here. This guarantees that the TransactionInfo stack
		// will be managed correctly even if no transaction was created by this aspect.
		txInfo.bindToThread();
		return txInfo;
	}

封装成为一个TransactionInfo粗惹怒到当前线程中来:

		private void bindToThread() {
			// Expose current TransactionStatus, preserving any existing TransactionStatus
			// for restoration after this transaction is complete.
             // 将之前的TransactionInfo保存
			this.oldTransactionInfo = transactionInfoHolder.get();            
             // 将新的TransactionInfo设置到当前线程中来
			transactionInfoHolder.set(this);
		}

这个transactionInfo有什么用呢?这涉及到事务传播机制。

也就是判断事务是否存在的地方:

		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(def, transaction, debugEnabled);
		}

从注释中可以看到检查传播行为在这里如何来进行决定。而判断是否存在事务的标记是什么?

	protected boolean isExistingTransaction(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
	}

判断数据库连接是否存在,如果存在还需要判断之前的事务是否是激活的。如果是激活的,那么当前事务决定怎么样来处理之前的事务呢?

看对应的处理方式,看下最普通的方式ISOLATION_DEFAULT:

		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);

这里直接开始着手构建事务状态对象:

	protected final DefaultTransactionStatus prepareTransactionStatus(
			TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
			boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {

		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
		prepareSynchronization(status, definition);
		return status;
	}

而这里又将回到之前的,注意一个判断:newSynchronization,这里会为FALSE,因为下面的代码

		boolean actualNewSynchronization = newSynchronization &&
				!TransactionSynchronizationManager.isSynchronizationActive();

返回之后,着手来构建一个新的TransactionInfo对象人,然后将其绑定到线程上去,但是也会将旧的TransactionInfo对象绑定到新的TransactionInfo对象上

		private void bindToThread() {
			// Expose current TransactionStatus, preserving any existing TransactionStatus
			// for restoration after this transaction is complete.
			this.oldTransactionInfo = transactionInfoHolder.get();
			transactionInfoHolder.set(this);
		}

那么这里首先来看下第二个事务的正常提交

无论是在提交还是回滚之前,首先会执行一行代码:

cleanupTransactionInfo(txInfo);
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
   if (txInfo != null) {
      txInfo.restoreThreadLocalStatus();
   }
}

然后继续看:

		private void restoreThreadLocalStatus() {
			// Use stack to restore old transaction TransactionInfo.
			// Will be null if none was set.
			transactionInfoHolder.set(this.oldTransactionInfo);
		}

将旧的transactionInfo重新绑定到线程上去。然后执行提交,也就是说这里会将所有的属于旧事务的信息都封装起来。

public final void commit(TransactionStatus status) throws TransactionException {
   DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
   // 是否局部有回滚标记
   if (defStatus.isLocalRollbackOnly()) {
      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);
}

那么开始来进行回滚

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");
         }
      }
      // 真正有用的代码

      // 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);
   }
}
posted @ 2022-06-29 14:47  写的代码很烂  阅读(9)  评论(0编辑  收藏  举报