• 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();
                }
            }
        }
            .....
            .....
    }
  • 相关阅读:
    使用CSDN Code将网站部署到Windows Azure Website上
    各种云计算虚拟机大比拼
    PLSQL无法连接64位Oracle数据库/Database下拉框为空的解决方法
    adb命令集锦
    Web常用函数介绍(LoadRunner相关)
    公开的免费WebService接口分享
    WEB服务器与应用服务器的区别
    电梯测试点有哪些?
    管理者必须知道的:你真的会惩罚你的团队吗?
    分享几个面试题的回答思路
  • 原文地址:https://www.cnblogs.com/mosmith/p/6791855.html
Copyright © 2020-2023  润新知