返回现场笔记
3 分钟阅读

探寻Spring事务的实现原理

从 Spring Boot 的自动配置入手,逐步拆解 Spring 事务的实现原理:@EnableTransactionManagement 如何通过 ProxyTransactionManagementConfiguration 注册切面与 TransactionInterceptor,以及 invokeWithinTransaction 中事务的创建、提交与回滚流程。

  • #spring
  • #transaction
  • #source-code

根据之前一篇文章的例子,我发现Spring Boot是自动完成事务的配置的,所以周末我特意翻了一段源码,探究了一把Spring Boot是如何完成这个自动配置的过程的。

首先在Spring Bootautoconfigure.jar依赖中的org.springframework.boot.autoconfigure.jdbc包下有一个DataSourceTransactionManagerAutoConfiguration类。Spring启动的时候就会根据是否添加了JDBC相关的包来判断是否要加载这个配置类中的配置。

@Configuration
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {

	@Configuration
	@ConditionalOnSingleCandidate(DataSource.class)
	static class DataSourceTransactionManagerConfiguration {

		private final DataSource dataSource;

		private final TransactionManagerCustomizers transactionManagerCustomizers;

		DataSourceTransactionManagerConfiguration(DataSource dataSource,
				ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
			this.dataSource = dataSource;
			this.transactionManagerCustomizers = transactionManagerCustomizers
					.getIfAvailable();
		}

		@Bean
		@ConditionalOnMissingBean(PlatformTransactionManager.class)
		public DataSourceTransactionManager transactionManager(
				DataSourceProperties properties) {
			DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
					this.dataSource);
			if (this.transactionManagerCustomizers != null) {
				this.transactionManagerCustomizers.customize(transactionManager);
			}
			return transactionManager;
		}

	}

	@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
	@Configuration
	@EnableTransactionManagement(proxyTargetClass = true)
	protected static class TransactionManagementConfiguration {

	}

}

以上这个配置类(DataSourceTransactionManagerAutoConfiguration)中有做了这样两个动作:

  1. 添加DataSourceTransactionManager事务管理器实例
  2. 触发@EnableTransactionManagement注解让当前项目启用事务。

这里先从第二个动作也就是触发@EnableTransactionManagement注解这个动作开始分析,看看启动的阶段Spring到底做了些什么。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {


	boolean proxyTargetClass() default false;

	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;

}

从中可以看出导入了另外一个实际的配置类TransactionManagementConfigurationSelector,这个类中仅有一个方法selectImports(),它会根据注解上的配置来选择Spring AOP中的通知配置,默认情况下是选择PROXY

@Override
protected String[] selectImports(AdviceMode adviceMode) {
	switch (adviceMode) {
		case PROXY:
			return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
		case ASPECTJ:
			return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
		default:
			return null;
	}
}

这里我们以ProxyTransactionManagementConfiguration为例:

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource());
		advisor.setAdvice(transactionInterceptor());
		advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {
		return new AnnotationTransactionAttributeSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor() {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource());
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}

}

ProxyTransactionManagementConfiguration中的三个配置分别定义:

  • transactionAttributeSource()方法在Spring容器中产生一个AnnotationTransactionAttributeSource实例,它能将我们常用的@Transactional注解转换成在Spring框架中使用的TransactionAttribute,如果我们需要在@Transactional注解上hack一些功能,也可以在这里重新实现TransactionAttribute类。
  • transactionAdvisor()方法根据transactionAttributeSource()定义了AOP的切点,也就是被@Transactional注解使用的方法。
  • transactionInterceptor()则定义了拦截器TransactionInterceptor,被之前定义的切点(也就是transactionAttributeSource()方法中定义的切面)拦截到的方法都会被送到这个类的实例中执行

到这里为止,事务拦截的部分已经完成了。再回到文章开头提到的另外一个配置:配置DataSourceTransactionManager事务管理器。

DataSourceTransactionManager继承关系

PlatformTransactionManagerSpring事务的核心接口,这个接口只包含了以下三个方法:

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;


void commit(TransactionStatus status) throws TransactionException;


void rollback(TransactionStatus status) throws TransactionException;

DataSourceTransactionManagerPlatformTransactionManager的最基本实现,**应用(Application)**操作事务的动作都会在这个类中进行。从配置中也不难发现,如果用户自己配置了自定义的事务管理器(比如JPA,JTA等等),Spring会忽略这个这个transactionManager()配置方法:

@ConditionalOnMissingBean(PlatformTransactionManager.class)

当了解Spring配置事务的过程之后,方法调用时事务的执行过程也就变得清晰起来了。

所有需要使用事务的方法(即标记了@Transactional的方法),都会在TransactionInterceptorinvoke()调用,然后在在TransactionAspectSupport类的invokeWithinTransaction()方法中被执行。

TransactionInterceptor#invoke调用的时序图

invokeWithinTransaction()方法中事务处理分成两种:

  • 标准事务处理
  • 异步事务处理

这里我们就分析下标准事务的部分,其核心代码(TransactionAspectSupport#invokeWithinTransaction前半段)如下:

// 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 = null;
	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
		completeTransactionAfterThrowing(txInfo, ex);
		throw ex;
	}
	finally {
		cleanupTransactionInfo(txInfo);
	}
	// ②判断提交事务
	commitTransactionAfterReturning(txInfo);
	eturn retVal;
}

这个部分中最重要的两个方法就是标记的两处,他们会分别去判断是否创建事务和提交事务。而invocation.proceedWithInvocation()则会执行应用中常规的方法,如果使用了JDBC的方法,SQL都会根据数据库的事务来处理。

/**
 * 判断是否要创建新的事务
 */
protected TransactionInfo createTransactionIfNecessary(
			PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {

    // If no name specified, apply method identification as transaction name.
	if (txAttr != null && txAttr.getName() == null) {
		txAttr = new DelegatingTransactionAttribute(txAttr) {
			@Override
			public String getName() {
				return joinpointIdentification;
			}
		};
	}

	TransactionStatus status = null;
	if (txAttr != null) {
		if (tm != null) {
			// 通过事务的属性,去上下文获取事务。如果不存在则会创建新的事务
			// 详细代码见 org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
			// 并在这个方法中开启事务 
			status = tm.getTransaction(txAttr);
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
						"] because no transaction manager has been configured");
			}
		}
	}
	return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

AbstractPlatformTransactionManager#getTransaction中会根据当前上下文的状态调用抽象方法doBegin()方法,也就是开启事务。

而在invokeWithinTransaction()方法标记的②处,会判断本次方法执行完是否要提交事务,这个方法中的核心是AbstractPlatformTransactionManager类的processCommit()方法:

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
	try {
		boolean beforeCompletionInvoked = false;
		try {
			prepareForCommit(status);
			triggerBeforeCommit(status);
			triggerBeforeCompletion(status);
			beforeCompletionInvoked = true;
			boolean globalRollbackOnly = false;
			if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
				globalRollbackOnly = status.isGlobalRollbackOnly();
			}
			if (status.hasSavepoint()) {
				if (status.isDebug()) {
					logger.debug("Releasing transaction savepoint");
				}
				status.releaseHeldSavepoint();
			}
			else if (status.isNewTransaction()) {
				if (status.isDebug()) {
					logger.debug("Initiating transaction commit");
				}
				// ③
				doCommit(status);
			}
			// Throw UnexpectedRollbackException if we have a global rollback-only
			// marker but still didn't get a corresponding exception from commit.
			if (globalRollbackOnly) {
				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 ex) {
			if (!beforeCompletionInvoked) {
				triggerBeforeCompletion(status);
			}
			doRollbackOnCommitException(status, ex);
			throw ex;
		}
		catch (Error err) {
			if (!beforeCompletionInvoked) {
				triggerBeforeCompletion(status);
			}
			doRollbackOnCommitException(status, err);
			throw err;
		}
		// 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);
	}
}

以上的整个流程中,由@Transaction注解转换的TransactionAttribute类以及由TransactionAttribute产生的TransactionStatus管理着Spring事务的状态和流程。当完成当前上下文的事务之后Spring就会根据事务的状态,判断是否要提交当前事务,如果事务完成还需要释放当前事务占用的相关资源。