美文网首页Spring Boot程序员
Explores The Ideas Behind Spring

Explores The Ideas Behind Spring

作者: whthomas | 来源:发表于2017-06-11 17:14 被阅读401次

    根据之前一篇文章的例子,我发现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就会根据事务的状态,判断是否要提交当前事务,如果事务完成还需要释放当前事务占用的相关资源。

    相关文章

      网友评论

        本文标题:Explores The Ideas Behind Spring

        本文链接:https://www.haomeiwen.com/subject/bighqxtx.html