You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
source-code-hunter/docs/Spring/SpringTransaction/Spring事务管理器的设计与实现.md

14 KiB

1 Spring事务处理的应用场景

下面我们以DataSourceTransactionManager事务管理器为例看一下在具体的事务管理器中如何实现事务创建、提交和回滚这些底 层的事务处理操作。DataSourceTransationManager和其他事务管理器一样如JtaTransactionManagerJpaTransactionManager和JdoTransactionManager都继承自AbstractPlatformManager作为一个基类AbstractPlatfromManager封装了Spring事务处理中通用的处理部分比如事务的创建、提交、回滚事务状态和信息的处理与线程的绑定等有了这些通用处理的支持对于具体的事务管理器而言它们只需要处理和具体数据源相关的组件设置就可以了比如在HibernateTransactionManager中就只需要配置好和Hibnernate事务处理相关的接口以及相关的设置。所以从PlatformTransactionManager组件的设计关系上我们也可以看到Spring事务处理的主要过程是分两个部分完成的通用的事务处理框架是在AbstractPlatformManager中完成而Spring的事务接口与数据源实现的接口多半是由具体 的事务管理器来完成它们都是作为AbstractPlatformManager的子类来是使用的。

可以看到在PlatformTransactionManager组件的设计中 通过PlatformTransactionManager接口 设计了一系列与事务处理息息相关的接口方法如getTransaction()、commit()、rollback()这些和事务处理相关的统一接口。对于这些接口的实现很大一部分是由AbstractTransactionManager抽象类来完成的这个类中的doGetTransaction()、doCommit()等方法和PlatformTransactionManager的方法 对应实现的是事务处理中相对通用的部分。在这个AbstractPlatformManager下为具体的数据源配置了不同的事务处理器以处理不同数据源的事务处理从而形成了一个从抽象到具体的事务处理中间平台设计使应用通过声明式事务处理即开即用事务处理服务隔离那些与特定的数据源相关的具体实现。

avatar

2 DataSourceTransactionManager的实现

我们先看一下DataSourceTransactionManager在这个事务管理器中它的实现直接与事务处理的底层实现相关。在事 务开始的时候会调用doBegin()方法首先会得到相对应的Connection然后可以根据事务设置的需要对Connection的相关属性进行配置比如将Connection的autoCommit功能关闭并对像TimeoutInSeconds这样的事务处理参数进行设置最后通过TransactionSynchronizationManager来对资源进行绑定。

从下面的代码中可以看到DataSourceTransactionManager作为AbstractPlatformTransactionManager的子类在AbstractPlatformTransactionManager中已经为事务实现设计好了一系列的模板方法比如 事务的提交、回滚处理等。在DataSourceTransactionManager中 可以看到对模板方法中一些抽象方法的具体实现。例如由DataSourceTransactionManager的doBegin()方法实现负责事务的创建工作。具体来说如果使用DataSource创建事务最终通过设置Connection的autoCommit属性来对事务处理进行配置。在实现过程中需要把数据库的Connection和当前的线程进行绑定。对于事务的提交和回滚都是通过直接调用Connection的提交和回滚来完成的在这个实现过程中如何取得事务处理场景中的Connection对象也是一个值得注意的地方。

上面介绍了使用DataSourceTransactionManager实现事务创建、提交和回滚的过程基本上与单独使用Connection实现事务处理是一样的也是通过设置autoCommit属性调用Connection的commit()和rollback()方法来完成的。而我们在声明式事务处理中看到的那些事务处理属性并不在DataSourceTransactionManager中完成这和我们在前面分析中看到的是一致的。

avatar

public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
		implements ResourceTransactionManager, InitializingBean {

	/** 这时注入的DataSource */
	private DataSource dataSource;

	/**
	 * 这里是产生Transaction的地方为Transaction的创建提供服务对数据库而言
	 * 事务工作是由Connection来完成的。这里把数据库的Connection对象放到了ConnectionHolder中
	 * 然后封装到一个DataSourceTransactionObject对象中在这个封装过程中增加了许多为事务处理服务的
	 * 控制数据
	 */
	@Override
	protected Object doGetTransaction() {
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		// 获取与当前线程绑定的数据库Connection这个Connection在第一个事务开始
		// 的地方与线程绑定
		ConnectionHolder conHolder =
			(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}
	
	/**
	 * 判断是否存在活跃的事务由ConnectionHolder的transactionActive属性来控制
	 */
	@Override
	protected boolean isExistingTransaction(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		return (txObject.getConnectionHolder() != null && txObject.getConnectionHolder().isTransactionActive());
	}
	
	/**
	 * 这里是处理事务开始的地方
	 * 此实现设置隔离级别,但忽略超时
	 */
	@Override
	protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (txObject.getConnectionHolder() == null ||
					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();

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

			// 这里是数据库Connection完成事务处理的重要配置需要把autoCommit属性关掉
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				con.setAutoCommit(false);
			}
			txObject.getConnectionHolder().setTransactionActive(true);

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

			// 把当前的数据库Connection与线程绑定
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
			}
		}

		catch (Throwable ex) {
			DataSourceUtils.releaseConnection(con, this.dataSource);
			throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
		}
	}
	
	/**
	 * 事务提交的具体实现
	 */
	@Override
	protected void doCommit(DefaultTransactionStatus status) {
		// 取得Connection以后通过Connection进行提交
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Committing JDBC transaction on Connection [" + con + "]");
		}
		try {
			con.commit();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not commit JDBC transaction", ex);
		}
	}
	
	/**
	 * 事务提交的具体实现通过Connection对象的rollback()方法实现
	 */
	@Override
	protected void doRollback(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
		}
		try {
			con.rollback();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
		}
	}
}

上面介绍了使用DataSourceTransactionManager实现事务创建、提交和回滚的过程基本上与单独使用Connection实现事务处理是一样的也是通过设置autoCommit属性调用Connection的commit()和rollback()方法来完成的。看到这里,大家一定会觉得非常的熟悉。而 我们在声明式事务处理中看到的那些事务处理属性并不在DataSourceTransactionManager中完成这和我们在前面分析中看到的是一致的。

3 小结

总体来说从声明式事务的整个实现中我们看到声明式事务处理完全可以看成是一个具体的Spring AOP应用。从这个角度来看Spring事 务处理的实现本身就为应用开发者提供了一个非常优秀的AOP应用参考实例。在Spring的声明式事务处理中采用了IoC容器的Bean配置为事务方法调用提供事务属性设置从而为应用对事务处理的使用提供方便。

有了声明式的使用方式可以把对事务处理的实现与应用代码分离出来。从Spring实现的角度来看声明式事务处理的大致实现过程是这样的在为事务处理配置好AOP的基础设施(比如对应的Proxy代理对象和事务处理Interceptor拦截器对象)之后首先需要完成对这些事务属性配置的读取这些属性的读取处理是在TransactionInterceptor中实现的在完成这些事务处理属性的读取之后Spring为事务处理的具体实现做好了准备。可以看到Spring声明式事务处理的过程同时也是一个整合事务处理实现到Spring AOP和IoC容器中去的过程。我们在整个过程中可以看到下面一些要点在这些要点中体现了对Spring框架的基本特性的灵活使用。

  • 如何封装各种不同事务处理环境下的事务处理具体来说作为应用平台的Spring它没法对应用使用什么样的事务处理环境做出限制这样对应用户使用的不同的事务处理器Spring事务处理平台都需要为用户提供服务。这些事务处理实现包括在应用中常见的DataSource的Connection、Hibermate的Transaction等Spring事务处理通过一种统一的方式把它们封装起来从而实现一个通用的事务处理过程实现这部分事务处理对应用透明使应用即开即用。
  • 如何读取事务处理属性值,在事务处理属性正确读取的基础上,结合事务处理代码,从而完成在既定的事务处理配置下,事务处理方法的实现。
  • 如何灵活地使用Spring AOP框架对事务处理进行封装提供给应用即开即用的声明式事务处理功能。

在这个过程中有几个Spring事务处理的核心类是我们需要关注的。其中包括TransactionInterceptor它是使用AOP实现声明式事务处理的拦截器封装了Spring对声明式事务处理实现的基本过程还包括TransactionAttributeSource和TransactionAttribute这两个类它们封装了对声明式事务处理属性的识别以及信息的读入和配置。我们看到的TransactionAttribute对象可以视为对事务处理属性的数据抽象如果在使用声明式事务处理的时候应用没有配置这些属性Spring将为用户提供DefaultTransactionAttribute对象该对象提供了默认的事务处理属性设置。

在事务处理过程中可以看到TransactionInfo和TransactionStatus这两个对象它们是存放事务处理信息的主要数据对象它们通过与线程的绑定来实现事务的隔离性。具体来说TransactionInfo对象本身就像是一个栈对应着每一次事务方法的调用它会保存每一次事务方法调用的事务处理信息。值得注意的是在TransactionInfo对象中它持有TransactionStatus对象这个TransactionStatus是非常重要的。由这个TransactionStatus来掌管事务执行的详细信息包括具体的事务对象、事务执行状态、事务设置状态等。

在事务的创建、启动、提交和回滚的过程中都需要与这个TransactionStatus对象中的数据打交道。在准备完这些与事务管理有关的数据之后具体的事务处理是由事务处理器TransactionManager来完成的。在事务处理器完成事务处理的过程中与具体事务处理器无关的操作都被封装到AbstractPlatformTransactionManager中实现了。这个抽象的事务处理器为不同的具体事务处理器提供了通用的事务处理模板它封装了在事务处理过程中与具体事务处理器无关的公共的事务处理部分。我们在具体的事务处理器(比如DataSourceTransactionManager和HibernateTransactionManager)的实现中可以看到,最为底层的事务创建、挂起、提交、回滚操作。

在Spring中也可以通过编程式的方法来使用事务处理器以帮助我们处理事务。在编程式的事务处理使用中, TransactionDefinition是定义事务处理属性的类。对于事务处理属性Spring还提供了一个默认的事务属性DefaultTransactionDefinition来供用户使用。这种事务处理方式在实现上看起来比声明式事务处理要简单但编程式实现事务处理却会造成事务处理与业务代码的紧密耦合因而不经常被使用。在这里我们之所以举编程式使用事务处理的例子是因为通过了解编程式事务处理的使用可以清楚地了解Spring统一实现事务处理的大致过程。

有了这个背景结合对声明式事务处理实现原理的详细分析比如在声明式事务处理中使用AOP对事务处理进行封装对事务属性配置进行的处理与线程绑定从而处理事务并发并结合事务处理器的使用等能够在很大程度上提高我们对整个Spring事务处理实现的理解。