背景

上文已经介绍了spring是基于代理来管理事务的。但如何管理事务的,其流程是怎样的。本文将带领大家逐步揭开spring事务管理的面纱。

事务管理可以简单的分解为三个问题?

- 什么时候打开事务
- 什么时候提交事务
- 什么时候发起事务回滚

流程

本文只关注spring如何管理事务,假设各位对spring生成代理的流程已经很了解了。


1. 在xml配置的声明式事务管理中,如下的配置中,spring调用的事务管理的代理类的方法是

TransactionInterceptor.invoke方法。该方法是事务管理的入口。属于接口MethodInterceptor的方法。

<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- 配置事务的传播特性 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="batch*" propagation="REQUIRED"/>
            <tx:method name="merge*" propagation="REQUIRED" />
            <tx:method name="get*" read-only="true"/>
            <tx:method name="load*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" read-only="true"/>
        </tx:attributes>
    </tx:advice>
  1. 在invoke方法是接口的入口,其调用了protected invokeWithinTransaction(Method method, Class
public Object invoke(final MethodInvocation invocation) throws Throwable {
        // Work out the target class: may be {@code null}.
        // The TransactionAttributeSource should be passed the target class
        // as well as the method, which may be from an interface.
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

        // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
            @Override
            public Object proceedWithInvocation() throws Throwable {
                return invocation.proceed();
            }
        });
    }
  1. 先贴出invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)方法
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
            throws Throwable {

        // 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);

        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);
            return retVal;
        }

        else {
            // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
                        new TransactionCallback<Object>() {
                            @Override
                            public Object doInTransaction(TransactionStatus status) {
                                TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                                try {
                                    return invocation.proceedWithInvocation();
                                }
                                catch (Throwable ex) {
                                    if (txAttr.rollbackOn(ex)) {
                                        // A RuntimeException: will lead to a rollback.
                                        if (ex instanceof RuntimeException) {
                                            throw (RuntimeException) ex;
                                        }
                                        else {
                                            throw new ThrowableHolderException(ex);
                                        }
                                    }
                                    else {
                                        // A normal return value: will lead to a commit.
                                        return new ThrowableHolder(ex);
                                    }
                                }
                                finally {
                                    cleanupTransactionInfo(txInfo);
                                }
                            }
                        });

                // Check result: It might indicate a Throwable to rethrow.
                if (result instanceof ThrowableHolder) {
                    throw ((ThrowableHolder) result).getThrowable();
                }
                else {
                    return result;
                }
            }
            catch (ThrowableHolderException ex) {
                throw ex.getCause();
            }
        }
    }

如上步所说,通过被代理类的类名+方法名来获取事务属性:final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass)
getTransactionAttributeSource()是继承的TransactionAspectSupport类的方法,返回NameMatchTransactionAttributeSource对象,这应该是在spring初始化时生成的。我们只关注如何通过类+方法获得事务属性。即TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass)

public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
        if (!ClassUtils.isUserLevelMethod(method)) {
            return null;
        }

        // Look for direct name match.
        String methodName = method.getName();
        TransactionAttribute attr = this.nameMap.get(methodName);

        if (attr == null) {
            // Look for most specific name match.
            String bestNameMatch = null;
            for (String mappedName : this.nameMap.keySet()) {
                if (isMatch(methodName, mappedName) &&
                        (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
                    attr = this.nameMap.get(mappedName);
                    bestNameMatch = mappedName;
                }
            }
        }

        return attr;
    }

该方法的大致流程是先对方法名做精确匹配;如果未找到,再通配符匹配。通配符匹配依据最长字符匹配原则

TransactionAttribute attr = this.nameMap.get(methodName),nameMap是一个HashMap

{load*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly, get*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly, delete*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT, merge*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT, update*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT, batch*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT, *=PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly, save*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT, find*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly}

spring对于事务定义了几个属性:
- Propagation:事务传播范围

PROPAGATION_REQUIRED:需要事务,当前没得事务,则开启一个新的事务


PROPAGATION_SUPPORTS: 支持事务执行,如果当前没得事务,则在无事务状态下执行


PROPAGATION_MANDATORY: 强制要求事务,如果当前没有事务,运行抛出异常


PROPAGATION_REQUIRES_NEW: 新事务,如果当前存在事务,将现有事务挂起,spring注释:Create a new transaction, suspending the current transaction if one exists


PROPAGATION_NOT_SUPPORTED:不支持事务,如果当前存在事务,则把事务挂起,spring注释: Existing synchronizations will be suspended and resumed appropriately


PROPAGATION_NEVER:无事务执行,如果当前存在事务,报错


PROPAGATION_NESTED:嵌套事务,只支持jdbc3.0以上,主要是针对jdbc3.0 savepoint特性开发,spring注释:this only applies to the JDBC
when working on a JDBC 3.0 driver
- Isolation:隔离级别


ISOLATION_READ_UNCOMMITTED:允许脏读,This level allows a row changed by one transaction to be read by another transaction before any changes in that row have been committed


ISOLATION_READ_COMMITTED:不允许脏读,This level only prohibits a transaction from reading a row with uncommitted changes in it


ISOLATION_REPEATABLE_READ:可重复读,但是幻读依然存在,This level includes the prohibitions in {@link #ISOLATION_REPEATABLE_READ}
and further prohibits the situation where one transaction reads all rows that
satisfy a {@code WHERE} condition, a second transaction inserts a row
that satisfies that {@code WHERE} condition, and the first transaction
re-reads for the same condition, retrieving the additional “phantom” row
in the second read
ISOLATION_SERIALIZABLE:串行,This level includes the prohibitions in {@link #ISOLATION_REPEATABLE_READ}
and further prohibits the situation where one transaction reads all rows that
satisfy a {@code WHERE} condition, a second transaction inserts a row
that satisfies that {@code WHERE} condition, and the first transaction
re-reads for the same condition, retrieving the additional “phantom” row
in the second read

  • Timeout:事务超时时间,默认-1,
  1. 根据上步的TransactionAttribute,查找事务管理器,我的工程中配置的是org.springframework.orm.hibernate5.HibernateTransactionManager。
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

抽象类TransactionAspectSupport维护了一个Map:transactionManagerCache,其主要作用是缓存事务管理器,避免每次使用都要通过BeanFactoryAnnotationUtils.qualifiedBeanOfType来查找,通过事务属性的Qualifier来查找对应的事务管理器。框架在初始化时,会初始化一个默认的事务管理器,如果事务Transactional没有指定使用哪个,则使用默认的,如果代码中维护了多个事务管理器,最好是以Qualifier区分,其使用方式如下

<tx:annotation-driven/>

<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="datasource1"></property>
    <qualifier value="datasource1Tx"/>
</bean>

<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="datasource2"></property>
    <qualifier value="datasource2Tx"/>
</bean>

使用时,用@Transactional(“datasource1Tx”)和@Transactional(“datasource2Tx”),来区别具体使用某个事务管理器。
其主要实现都是在TransactionAspectSupport.determineTransactionManager。

final PlatformTransactionManager tm = determineTransactionManager(txAttr)
  1. 根据代理对象的方法和类创建一个标识事务开始位置点的string,=类名.方法名,com.base.service.impl.TestServiceImpl.save
  2. 步骤5是判断当前的事务管理器是否是CallbackPreferringPlatformTransactionManager,一般来说不是。CallbackPreferringPlatformTransactionManager 的简单理解是支持回调的事务管理器。这是spring解释;Implementors of this interface automatically express a preference for callbacks over programmatic getTransaction, commit and rollback calls。这部分我暂时不太理解,就不不在此描述,简单来说,框架会调用应用的commit,rollback方法。

总结

spring 管理事务第一阶段非常简单,流程如下:

1. 代理对象的方法查找对应的事务属性,获取TrasactionArribute。TrasactionArribute主要属性:TrasactionDefine(事务传播范围,事务隔离级别,事务超时时间),qulifier(决定使用哪个事务管理器)

2. 依据TrasactionArribute选择TransactionMannager


spring 在这一块的结构非常清晰,还是比较有借鉴价值的。

1. TransactionInterceptor继承TransactionAspectSupport,扩展MethodInterceptor(aop接口),TransactionAspectSupport作为抽象类实现了大部分的业务逻辑,TransactionInterceptor作为aop模块和事务模块之间的桥梁,其实现非常简单,实现了业务逻辑上的解耦,之后的修改可以集中在不同模块上,而无需改动桥梁。