Explores The Ideas Behind Spring Transaction

根据之前一篇文章的例子,我发现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) {
            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.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事务管理器。

Explores The Ideas Behind Spring Transaction_第1张图片
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()方法中被执行。

Explores The Ideas Behind Spring Transaction_第2张图片
`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就会根据事务的状态,判断是否要提交当前事务,如果事务完成还需要释放当前事务占用的相关资源。

你可能感兴趣的:(Explores The Ideas Behind Spring Transaction)