Spring @Transactional原理

Posted mosmith

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring @Transactional原理相关的知识,希望对你有一定的参考价值。

今天想用一下Spring的Transaction Manager,但中间遇到一个问题,但文档上讲得不是很清楚,于是乎只得自己去扒代码来看了。

首先从配置入手,启用Spring的TransactionManagement需要在Configuration Bean上加上@EnableTransactionManagement注解,或者在XML配置文件中加上<tx:annotation-driver />元素来启用事务支持,这样我们将会在Spring Context里面注册事务支持所需要的组件,比如Interceptor, Advisor等等。

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

    /**
     * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
     * opposed to standard Java interface-based proxies ({@code false}). The default is
     * {@code false}. <strong>Applicable only if {@link #mode()} is set to
     * {@link AdviceMode#PROXY}</strong>.
     * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
     * Spring-managed beans requiring proxying, not just those marked with
     * {@code @Transactional}. For example, other beans marked with Spring‘s
     * {@code @Async} annotation will be upgraded to subclass proxying at the same
     * time. This approach has no negative impact in practice unless one is explicitly
     * expecting one type of proxy vs another, e.g. in tests.
     */
    boolean proxyTargetClass() default false;

    /**
     * Indicate how transactional advice should be applied. The default is
     * {@link AdviceMode#PROXY}.
     * @see AdviceMode
     */
    AdviceMode mode() default AdviceMode.PROXY;

    /**
     * Indicate the ordering of the execution of the transaction advisor
     * when multiple advices are applied at a specific joinpoint.
     * The default is {@link Ordered#LOWEST_PRECEDENCE}.
     */
    int order() default Ordered.LOWEST_PRECEDENCE;

}

 

上面的又引入TransactionManagementConfigurationSelector配置,从下面TransactionManagementConfigurationSelector的内容可以看出,当我们使用了aspectj的时候,它导入的是org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration,这里我们重点关注AnnotationTransactionAspect和txManager。txManager从上下文中的TransactionManagementConfigurer(这是一个泛型注入)来获取,其中txAspect.setTransactionManager方法是继承结构上基类TransactionAspectSupport中的方法。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    /**
     * {@inheritDoc}
     * @return {@link ProxyTransactionManagementConfiguration} or
     * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
     * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
     */
    @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;
        }
    }
}
@Configuration
public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public AnnotationTransactionAspect transactionAspect() {
        AnnotationTransactionAspect txAspect = AnnotationTransactionAspect.aspectOf();
        if (this.txManager != null) {
            txAspect.setTransactionManager(this.txManager);
        }
        return txAspect;
    }

}


@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {

    protected AnnotationAttributes enableTx;

    /**
     * Default transaction manager, as configured through a {@link TransactionManagementConfigurer}.
     */
    protected PlatformTransactionManager txManager;


    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        this.enableTx = AnnotationAttributes.fromMap(
                importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
        if (this.enableTx == null) {
            throw new IllegalArgumentException(
                    "@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
        }
    }

    @Autowired(required = false)
    void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
        if (CollectionUtils.isEmpty(configurers)) {
            return;
        }
        if (configurers.size() > 1) {
            throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
        }
        TransactionManagementConfigurer configurer = configurers.iterator().next();
        this.txManager = configurer.annotationDrivenTransactionManager();
    }


    @Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionalEventListenerFactory transactionalEventListenerFactory() {
        return new TransactionalEventListenerFactory();
    }

}

 

AnnotationTransactionAspect这个类比较奇怪,它是aspect4j格式的AnnotationTransactionAspect.aj编译过来的,下面是它的源码,可见它就是适配aspect4j的,定义拦截点(pointcut)以及拦截操作,拦截点主要是@Transactional标记的方法和类,拦截操作主要是调用TransactionAspectSupport.invokeWithinTransaction方法。

public aspect AnnotationTransactionAspect extends AbstractTransactionAspect {
public AnnotationTransactionAspect() {
super(new AnnotationTransactionAttributeSource(false));
}
/**
 * Matches the execution of any public method in a type with the
 * Transactional annotation, or any subtype of a type with the
 * Transactional annotation.
 */
private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
execution(public * ((@Transactional *)+).*(..)) && @this(Transactional);
/**
 * Matches the execution of any method with the 
 * Transactional annotation.
 */
private pointcut executionOfTransactionalMethod() :
execution(* *(..)) && @annotation(Transactional);
/**
 * Definition of pointcut from super aspect - matched join points
 * will have Spring transaction management applied.
 */
protected pointcut transactionalMethodExecution(Object txObject) :
(executionOfAnyPublicMethodInAtTransactionalType()
 || executionOfTransactionalMethod() )
 && this(txObject);
}
public abstract aspect AbstractTransactionAspect extends TransactionAspectSupport implements DisposableBean {

    /**
     * Construct the aspect using the given transaction metadata retrieval strategy.
     * @param tas TransactionAttributeSource implementation, retrieving Spring
     * transaction metadata for each joinpoint. Implement the subclass to pass in
     * {@code null} if it is intended to be configured through Setter Injection.
     */
    protected AbstractTransactionAspect(TransactionAttributeSource tas) {
        setTransactionAttributeSource(tas);
    }

    @Override
    public void destroy() {
        clearTransactionManagerCache(); // An aspect is basically a singleton
    }

    @SuppressAjWarnings("adviceDidNotMatch")
    Object around(final Object txObject): transactionalMethodExecution(txObject) {
        MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
        // Adapt to TransactionAspectSupport‘s invokeWithinTransaction...
        try {
            return invokeWithinTransaction(methodSignature.getMethod(), txObject.getClass(), new InvocationCallback() {
                public Object proceedWithInvocation() throws Throwable {
                    return proceed(txObject);
                }
            });
        }
        catch (RuntimeException ex) {
            throw ex;
        }
        catch (Error err) {
            throw err;
        }
        catch (Throwable thr) {
            Rethrower.rethrow(thr);
            throw new IllegalStateException("Should never get here", thr);
        }
    }

    /**
     * Concrete subaspects must implement this pointcut, to identify
     * transactional methods. For each selected joinpoint, TransactionMetadata
     * will be retrieved using Spring‘s TransactionAttributeSource interface.
     */
    protected abstract pointcut transactionalMethodExecution(Object txObject);


    /**
     * Ugly but safe workaround: We need to be able to propagate checked exceptions,
     * despite AspectJ around advice supporting specifically declared exceptions only.
     */
    private static class Rethrower {

        public static void rethrow(final Throwable exception) {
            class CheckedExceptionRethrower<T extends Throwable> {
                @SuppressWarnings("unchecked")
                private void rethrow(Throwable exception) throws T {
                    throw (T) exception;
                }
            }
            new CheckedExceptionRethrower<RuntimeException>().rethrow(exception);
        }
    }

}

 

TransactionAspectSupport.invokeWithinTransaction的代码就比较清晰了,获取TransactionManager并调用它的execute方法,传入我们的callback,在callback中执行我们的事务方法。determineTransactionManager方法用于获取TransactionManagaer,当我们没有从TransactionManagementConfigurer中配置目标TransactionManager,通过其它方式确定一个TransactionManager,比如什么都没有配置的时候,默认从上下文(BeanFactory)中获取类型为PlatformTransactionManager的Bean作为默认的TransactionManager

public class TransactionAspectSupport {
        .....
        .....
    /**
     * General delegate for around-advice-based subclasses, delegating to several other template
     * methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
     * as well as regular {@link PlatformTransactionManager} implementations.
     * @param method the Method being invoked
     * @param targetClass the target class that we‘re invoking the method on
     * @param invocation the callback to use for proceeding with the target invocation
     * @return the return value of the method, if any
     * @throws Throwable propagated from the target 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();
            }
        }
    }
        .....
        .....
}

 

以上是关于Spring @Transactional原理的主要内容,如果未能解决你的问题,请参考以下文章

spring transaction注解原理

Spring @Transactional ——事务回滚

Spring @Transactional ——事务回滚

#yyds干货盘点# 老王读Spring AOP-5@Transactional产生AOP代理的原理

#yyds干货盘点# 老王读Spring AOP-5@Transactional产生AOP代理的原理

@Transactional的使用