Spring事务原理源码简述


spring框架的事务其实也是基于jdbc的事务,而一般的jdbc事务代码如下形式


        try{
            connection.setAutoCommit(false);//turn off autocommit transaction
            ...数据操作
            connection.commit();//commit transaction by self
        }
        catch (Exception e){
            connection.rollback();
            e.printStackTrace();
        }

如果每次数据库操作都按照这种原生的方式来写,那么代码里面会多出非常多冗余代码,因此spring框架利用自身aop机制,为使用者提供了@transactional注解,实现注解式事务管理
spring的事务其实也是基于上面原生jdbc来做的,因此事务的管理本质上还是对jdbc connection的管理,核心的类和方法如下:

AbstractPlatformTransactionManager 看名字知道这是一个定义基本方法和流程的抽象类,它串联了一个事务的基本操作,包括提交回滚等,可以看作是上面原生jdbc的抽象

DataSourceTransactionManager doBegin() 这是执行事务操作的地方,代码如下,和原生jdbc的connection.setAutoCommit(false)对应,但其中还有一步关键步骤,获取连接

@Override
	protected void doBegin(Object transaction, TransactionDefinition definition) {
                //DataSourceTransactionObject是一个包装类,其中有各种事务的属性和连接包装类
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                                //获取连接,包装在ConnectionHolder中
				Connection newCon = obtainDataSource().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();

			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
			txObject.setPreviousIsolationLevel(previousIsolationLevel);
			txObject.setReadOnly(definition.isReadOnly());

			// 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.
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}
		}

		catch (Throwable ex) {
			if (txObject.isNewConnectionHolder()) {
				DataSourceUtils.releaseConnection(con, obtainDataSource());
				txObject.setConnectionHolder(null, false);
			}
			throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
		}
	}

这个方法中获取连接是关键,看看spring是如何实现不同请求事务的隔离的,Connection newCon = obtainDataSource().getConnection();这一句获取了一个数据库连接,而下面有这样一句,还带着注释

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

注释翻译过来就是把connection和线程绑定起来,显然就是不同事务隔离的关键,点进去看看:

	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");
...
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 [" + Thread.currentThread().getName() + "]");
		}
		 
	}

resources这个变量是关键,他是一个NamedThreadLocal,其实就是ThreadLocal加个名字,正是ThreadLocal实现了connection和线程的对应绑定,要使用时候调用doGetResource(Object actualKey) 方法获取即可

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/279038.html

(0)
上一篇 2022年8月5日 14:47
下一篇 2022年8月5日 15:18

相关推荐

发表回复

登录后才能评论