Spring事务属性详解及其实现原理

297 阅读6分钟

事务注解@Transactional及其属性

Transactional的属性:

image.png

属性名称功能描述默认值
transactionManager(value的别名)指定事务管理器”“
value指定事务管理器”“
propagation事务的传播行为,规定事务方法相互调用的时候的解决方法Propagation.REQUIRED
isolation事务的隔离级别Isolation.DEFAULT
timeout事务的超时时,如果超过该时间限制但事务还没有完成,则自动回滚事务-1
readOnly指定事务是否为只读事务,声明只读可加快事务执行速度false
rollbackFor抛出指定的异常类型,不回滚事务{}(空)
noRollbackFor用于指定能够触发事务回滚的异常类型,可以指定多个异常类型{}(空)
rollbackForClassName设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚{}(空)
noRollbackForClassName不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚{}(空)

propagation类型

规定多个事务方法之前存在相互调用时如何实现事务

image.png

isolation类型

image.png

注解源码


@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Inherited

@Documented

public @interface Transactional {

/**

* Alias for {@link #transactionManager}.

* @see #transactionManager

*/

@AliasFor("transactionManager")

String value() default "";

/**

* A <em>qualifier</em> value for the specified transaction.

* <p>May be used to determine the target transaction manager, matching the

* qualifier value (or the bean name) of a specific

* {@link org.springframework.transaction.TransactionManager TransactionManager}

* bean definition.

* @since 4.2

* @see #value

* @see org.springframework.transaction.PlatformTransactionManager

* @see org.springframework.transaction.ReactiveTransactionManager

*/

@AliasFor("value")

String transactionManager() default "";

/**

* The transaction propagation type.

* <p>Defaults to {@link Propagation#REQUIRED}.

* @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()

*/

Propagation propagation() default Propagation.REQUIRED;

/**

* The transaction isolation level.

* <p>Defaults to {@link Isolation#DEFAULT}.

* <p>Exclusively designed for use with {@link Propagation#REQUIRED} or

* {@link Propagation#REQUIRES_NEW} since it only applies to newly started

* transactions. Consider switching the "validateExistingTransactions" flag to

* "true" on your transaction manager if you'd like isolation level declarations

* to get rejected when participating in an existing transaction with a different

* isolation level.

* @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()

* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction

*/

Isolation isolation() default Isolation.DEFAULT;

/**

* The timeout for this transaction (in seconds).

* <p>Defaults to the default timeout of the underlying transaction system.

* <p>Exclusively designed for use with {@link Propagation#REQUIRED} or

* {@link Propagation#REQUIRES_NEW} since it only applies to newly started

* transactions.

* @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()

*/

int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

/**

* A boolean flag that can be set to {@code true} if the transaction is

* effectively read-only, allowing for corresponding optimizations at runtime.

* <p>Defaults to {@code false}.

* <p>This just serves as a hint for the actual transaction subsystem;

* it will <i>not necessarily</i> cause failure of write access attempts.

* A transaction manager which cannot interpret the read-only hint will

* <i>not</i> throw an exception when asked for a read-only transaction

* but rather silently ignore the hint.

* @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()

* @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()

*/

boolean readOnly() default false;

/**

* Defines zero (0) or more exception {@link Class classes}, which must be

* subclasses of {@link Throwable}, indicating which exception types must cause

* a transaction rollback.

* <p>By default, a transaction will be rolling back on {@link RuntimeException}

* and {@link Error} but not on checked exceptions (business exceptions). See

* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}

* for a detailed explanation.

* <p>This is the preferred way to construct a rollback rule (in contrast to

* {@link #rollbackForClassName}), matching the exception class and its subclasses.

* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}.

* @see #rollbackForClassName

* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)

*/

Class<? extends Throwable>[] rollbackFor() default {};

/**

* Defines zero (0) or more exception names (for exceptions which must be a

* subclass of {@link Throwable}), indicating which exception types must cause

* a transaction rollback.

* <p>This can be a substring of a fully qualified class name, with no wildcard

* support at present. For example, a value of {@code "ServletException"} would

* match {@code javax.servlet.ServletException} and its subclasses.

* <p><b>NB:</b> Consider carefully how specific the pattern is and whether

* to include package information (which isn't mandatory). For example,

* {@code "Exception"} will match nearly anything and will probably hide other

* rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"}

* were meant to define a rule for all checked exceptions. With more unusual

* {@link Exception} names such as {@code "BaseBusinessException"} there is no

* need to use a FQN.

* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}.

* @see #rollbackFor

* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)

*/

String[] rollbackForClassName() default {};

/**

* Defines zero (0) or more exception {@link Class Classes}, which must be

* subclasses of {@link Throwable}, indicating which exception types must

* <b>not</b> cause a transaction rollback.

* <p>This is the preferred way to construct a rollback rule (in contrast

* to {@link #noRollbackForClassName}), matching the exception class and

* its subclasses.

* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}.

* @see #noRollbackForClassName

* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)

*/

Class<? extends Throwable>[] noRollbackFor() default {};

/**

* Defines zero (0) or more exception names (for exceptions which must be a

* subclass of {@link Throwable}) indicating which exception types must <b>not</b>

* cause a transaction rollback.

* <p>See the description of {@link #rollbackForClassName} for further

* information on how the specified names are treated.

* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}.

* @see #noRollbackFor

* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)

*/

String[] noRollbackForClassName() default {};

}

事务的实现原理

Spring 中支持两种事务处理的方式,分别是声明式事务处理编程式事务处理,两者相对于开发人员来讲差别很大,但是对于Spring 中的实现来讲,大同小异。核心原理都是通过拦截器来实现的,TransactionInterceptor 支撑着整个事务功能的架构。TransactionInterceptor类继承自 MethodInterceptor,所以调用该类是从其 invoke 方法开始的:

@Override
@Nullable

public Object invoke(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, invocation::proceed);

}
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
                                             final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();

    //1.获取事务属性
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

    //2. 加载配置中配置的 TransactionManager
    final TransactionManager tm = determineTransactionManager(txAttr);

    if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
        ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
            if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
                throw new TransactionUsageException(
                        "Unsupported annotated transaction on suspending function detected: " + method +
                                ". Use TransactionalOperator.transactional extensions instead.");
            }

            ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());

            if (adapter == null) {
                throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
                        method.getReturnType());

            }
            return new ReactiveTransactionSupport(adapter);
        });
        return txSupport.invokeWithinTransaction(
                method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);

    }
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    ////构造方法唯一标识(类.方法,如 service.UserServiceImpl.save)
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    //声明式事务处理
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        //创建TransactionInfo
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
        Object retVal;

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

        if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
            // Set rollback-only in case of Vavr failure matching our rollback rules...
            TransactionStatus status = txInfo.getTransactionStatus();
            if (status != null && txAttr != null) {
                retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
            }
        }

        //提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    //编程式事务处理
    else {
        final ThrowableHolder throwableHolder = new ThrowableHolder();
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        try {
            Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
                try {
                    Object retVal = invocation.proceedWithInvocation();
                    if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
                        // Set rollback-only in case of Vavr failure matching our rollback rules...
                        retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                    }
                    return retVal;
                } 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.
                        throwableHolder.throwable = ex;

                        return null;

                    }

                } finally {
                    cleanupTransactionInfo(txInfo);
                }

            });

            // Check result state: It might indicate a Throwable to rethrow.
            if (throwableHolder.throwable != null) {
                throw throwableHolder.throwable;

            }

            return result;

        } catch (ThrowableHolderException ex) {
            throw ex.getCause();

        } catch (TransactionSystemException ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                ex2.initApplicationException(throwableHolder.throwable);

            }

            throw ex2;

        } catch (Throwable ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
            }
            throw ex2;
        }
    }
}

在 invoke 中我们也可以看到这两种方式的实现。考虑到对事务的应用声明式的事务比编程式处理使用起来方便,也相对流行些,我们就以此种方式进行分析。对于声明式的事务处理主要有以下几个步骤。

  1. 获取事务的属性。对于事务处理来说,最基础或者说最首要的工作便是获取事务属性了,这是支撑整个事务功能的基石,如果没有事务属性,其他功能也无从谈起
  2. 加载配置中配置的 TransactionManager
  3. 不同的事务处理方式使用不同的逻辑。对于声明式事务的处理与编程式事务的处理,第一点区别在于事务属性上,因为编程式的事务处理是不需要有事务属性的,第二点区别就是在 TransactionManager上,CallbackPreferring PlatformTransactionManager 实现 PlatformTransactionManager 接口,暴露出一个方法用于执行事务处理中的回调。所以,这两种方式都可以用作事务处理方式的判断。
  4. 在目标方法执行前获取事务并收集事务信息。事务信息与事务属性并不相同,也就是 TransactionInfo 与 TransactionAttribute 并不相同,TransactionInfo 中包含 TransactionAttribute 信息,但是,除了 TransactionAttribute 外还有其他事务信息,例如 PlatformTransactionManager 以及 TransactionStatus相关信息。
  5. 执行目标方法。
  6. 一旦出现异常,尝试异常处理。并不是所有异常,Spring都会将其回滚,默认只对 RuntimeException 回滚。
  7. 提交事务前的事务信息清除。
  8. 提交事务。 【参考】

【1】《Spring源码深度解析》