Spring 声明式事务

2020-02-22  本文已影响0人  饿了就下楼

Spring事务与AOP的联系

  Spring中声明式事务是借助于AOP来实现的,在AOP中我们可以对切面进行配置从而实现对于方法的前后增强,spring事务也是需要切面完成,由于可以将事务理解为某一特定的切面,所以在Spring事务实现中,通过一种切面TransactionInterceptor来实现(它实现了MethodInterceptor接口)。并且可以将配置的属性等信息放入TransactionInterceptor实例中,便可以实现通过配置来完成事务的处理。
  
  Spring事务是借助于TransactionProxyFactoryBean这个FactoryBean来实现的。我们通常可以为其配置一下属性

target:需要进行事务处理的目标对象
transactionManager :具体的事务处理器
transactionAttributes / transactionAttributeSource : 具体属性配置,例如事务传播规则等。前者最终转换成后者。
pointcut :设置切入点,指定为哪些方法进行事务处理。

首先看一下TransactionProxyFactoryBean的源码

public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
        implements BeanFactoryAware {

    private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

    private Pointcut pointcut;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionInterceptor.setTransactionManager(transactionManager);
    }

    public void setTransactionAttributes(Properties transactionAttributes) {
        this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
    }

    public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
        this.transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
    }

    public void setPointcut(Pointcut pointcut) {
        this.pointcut = pointcut;
    }
        
         /*省略代码*/
        
}

   通过源码大致可以看出,配置的target和pointcut属性是通过TransactionProxyFactoryBean中的字段来管理的(target属性由父类管理),而transactionManagertransactionAttributes则是注入给了TransactionInterceptor实例,并且配置的transactionAttributes 最终会转换成transactionAttributeSource管理。。

   既然TransactionProxyFactoryBean是一个FactoryBean,我们就想到去看一下他的getObject方法。

    public Object getObject() {
        if (this.proxy == null) {
            throw new FactoryBeanNotInitializedException();
        }
        return this.proxy;
    }

但是发现,在getObject方法中需要返回proxy对象,如果为空,则抛异常,也就是在此方法执行前,proxy已经创建好了,那么是在哪里呢。可以看到TransactionProxyFactoryBean的父类实现了InitializingBean并且重写了afterPropertiesSet方法。

    public void afterPropertiesSet() {
        /*省略代码*/
        if (this.proxyClassLoader == null) {
            this.proxyClassLoader = ClassUtils.getDefaultClassLoader();
        }

        ProxyFactory proxyFactory = new ProxyFactory();
        if (this.preInterceptors != null) {
            for (Object interceptor : this.preInterceptors) {
                proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
            }
        }
        // Add the main interceptor (typically an Advisor).
        proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));
        if (this.postInterceptors != null) {
            for (Object interceptor : this.postInterceptors) {
                proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
            }
        }
        proxyFactory.copyFrom(this);
        TargetSource targetSource = createTargetSource(this.target);
        proxyFactory.setTargetSource(targetSource);
        if (this.proxyInterfaces != null) {
            proxyFactory.setInterfaces(this.proxyInterfaces);
        }
        else if (!isProxyTargetClass()) {
            // Rely on AOP infrastructure to tell us what interfaces to proxy.
            proxyFactory.setInterfaces(
                    ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
        }
        this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
    }

在这个方法中,通过一个ProxyFactory 来创建代理对象。程序向ProxyFactory 中添加了Advisor实例,那么它所创建的代理对象势必也就会与添加的Advisor实例关联起来。我们需要注意的是createMainInterceptor这个方法(由TransactionProxyFactoryBean重写)。

    @Override
    protected Object createMainInterceptor() {
        this.transactionInterceptor.afterPropertiesSet();
        if (this.pointcut != null) {
            return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
        }
        else {
            // Rely on default pointcut.
            return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
        }
    }

通过判断是否配置了切入点,来决定将切面封装为什么类型的Advisor(封装Advisor的时候需要传入advice实例,我们在aop部分也提到过,MethodInterceptor实例本身也是advice),我们可以稍微留意一下这两个Advisor实例。最终返回的Advisor实例会被添加到ProxyFactory的Advisor列表中。除了advisor链表之外,ProxyFactory还配置了例如targetSource,interfaces等(感觉似乎跟aop中ProxyFactoryBean干的事儿有点相似),最后调用了proxyFactory.getProxy方法创建代理对象。我们点开getProxy方法发现神奇的一幕:

    public Object getProxy(ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

执行到这里的逻辑跟aop好像很像啊。我们刚才偶然感觉ProxyFactory进行了一些配置,配置的内容跟ProxyFactoryBean有点相似,然后他俩还都要获取Aop代理对象来创建代理。我们查看ProxyFactoryProxyFactoryBean,发现他俩都是继承自ProxyCreatorSupport。。。我说呢,难怪他俩配置的东西很一致。虽然从名字上来讲,感觉TransactionProxyFactoryBeanProxyFactoryBean像是兄弟,但是发现不是诶,只不过TransactionProxyFactoryBean拉拢了ProxyFactoryBean的兄弟ProxyFactory来帮他干事(进行事务过程的代理对象创建)的。既然ProxyFactory跟aop一样,底层都是相同方式创建了代理,那么代理创建代理的过程就跟aop那一套没啥区别,这里就不分析了。但是代理所执行的功能不同,也就是执行的切面不一样啊,毕竟aop是通过配置的advice、advisor、MethodInterceptor等玩意儿最终封装为了MethodInterceptor来进行前/后增强,而事务中,主要是TransactionInterceptor

TransactionAttributeSource

  前文讲到,可通过transactionAttributes / transactionAttributeSource来进行事务配置以决定对哪些方法进行事务处理,以及事务处理过程中的一些配置。通常采用transactionAttributes,当把transactionAttributes注入给TransactionInterceptor时,底层转换为transactionAttributeSource。

        /*transactionInterceptor.setTransactionAttributes方法的实现(实际在父类中)*/
    public void setTransactionAttributes(Properties transactionAttributes) {
        NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
        tas.setProperties(transactionAttributes);
        this.transactionAttributeSource = tas;
    }

    public void setProperties(Properties transactionAttributes) {
        TransactionAttributeEditor tae = new TransactionAttributeEditor();
        Enumeration propNames = transactionAttributes.propertyNames();
        while (propNames.hasMoreElements()) {
            String methodName = (String) propNames.nextElement();
            String value = transactionAttributes.getProperty(methodName);
            tae.setAsText(value);
            TransactionAttribute attr = (TransactionAttribute) tae.getValue();
            addTransactionalMethod(methodName, attr);
        }
    }

    public void addTransactionalMethod(String methodName, TransactionAttribute attr) {
        if (logger.isDebugEnabled()) {
            logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]");
        }
        this.nameMap.put(methodName, attr);
    }

上面的代码完成了配置的解析,最终可以看到配置的属性转换到了NameMatchTransactionAttributeSource实例,并将配置了事务处理的方法以及对其进行的事务属性配置放入了一个map中。TransactionAttribute 长什么样子呢?

TransactionAttributeSourceAdvisor

  再讲TransactionInterceptor之前,有必要先去了解TransactionAttributeSourceAdvisor,前文提到TransactionInterceptor会被封装为Advisor,而当我们没有配置PointCut的时候,生成的便是TransactionAttributeSourceAdvisor,最终加入到Advisor链表中。一个Advisor,封装了pointcut和advice(这是针对PointcutAdvisor来说的),而TransactionAttributeSourceAdvisor已知封装了一个advice也就是TransactionInterceptor,那么下面我们就关注一下pointcut,以及它是如何判断一个目标方法是否需要被执行事务切面的。

        /*TransactionAttributeSourceAdvisor中定义的pointcut*/        

    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
        @Override
        protected TransactionAttributeSource getTransactionAttributeSource() {
            return (transactionInterceptor != null ? transactionInterceptor.getTransactionAttributeSource() : null);
        }
    };

代码中可以看出,TransactionAttributeSourceAdvisor中定义了TransactionAttributeSourcePointcut 这么一个pointcut。matches方法决定了是否需要对一个方法进行事务管理。

        /*TransactionAttributeSourcePointcut中的matches方法*/     
 
    public boolean matches(Method method, Class targetClass) {
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

在matches方法中,通过调用被重写的getTransactionAttributeSource,来获取TransactionInterceptor配置的transactionAttributeSource。我们配置了事务属性后,matches方法中就可以拿到tas实例(如果没有配置的话tas为空,matches返回为true表明所有方法都可以match成功然后进行事务),并且根据配置的属性判断当前method是否允许进行事务处理。

    public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
        // 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;
    }

判断一个方法是否进行事务处理的逻辑很简单:如果能够从nameMap中取出该方法对应的属性配置,那就返回属性,则tas.getTransactionAttribute(method, targetClass) != null成立,matches方法返回true。如果nameMap中没有,则判断是否是通配符匹配的,并返回最佳匹配结果。如果依然匹配不了,则最终matches返回false,此方法不会执行事务处理。

TransactionInterceptor和事务的执行

  上面讲完了一些spring事务中用到的组件,并且也已经可以获取到代理对象。创建代理对象的过程省略了,跟aop差不多。此时,代理对象便可以通过TransactionProxyFactoryBean的getObject来获取了。随后程序运行需要调用方法时,动态代理的invoke(对于jdk动态代理来说)方法会执行。回忆一下aop中invoke方法怎么执行的:遍历advisor链表,如果一个advisor中的pointcut认为当前方法可以添加切面则会将其中的advice对象封装为MethodInterceptor(也就是责任链模式中的执行链的节点),最终返回该方法的Interceptor执行链,决定了要进行的切面处理。随后通过责任链的模式进行切面的前置/后置/环绕等处理(会调用拦截器的invoke方法)。spring事务也是这个流程,不过不同点在于切面用TransactionInterceptor体现。
  我们就看一下TransactionInterceptor的invoke方法。这里稍微提一下的一点:当调用了invoke方法的时候,说明此时执行链已经在执行,那么表明此方法一定是需要进行事务处理的,因为不需事务处理的方法一开始就会被pointcut过滤掉,也就不会有执行链的。

    public Object invoke(final MethodInvocation invocation) throws Throwable {
    
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
            public Object proceedWithInvocation() throws Throwable {
                return invocation.proceed();
            }
        });
    }

首先获取到被代理对象的实际类型,然后调用了一个方法,传入了正在调用的目标方法,目标类型,以及一个回调,当回调方法proceedWithInvocation被执行的时候,会使得执行链先前继续(invocation.proceed在aop部分就是让责任链继续向前执行)。进入invokeWithinTransaction。

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

整体结构就是一个大的if-else。对于DataSourceTransactionManager来说,它不是CallbackPreferringPlatformTransactionManager这种类型,因此进入if分支,我们重点只关注一下DataSourceTransactionManager就好了。 createTransactionIfNecessary会根据属性判断是否需要新建事务并将事务状态返回TransactionInfo。随后看到invocation.proceedWithInvocation()回调函数的执行,它完成的是执行链向下继续执行,完成其它逻辑并最终执行目标方法(看到这里,我们可以知道,事务处理相当于是为目标方法增加了一个环绕通知,目标方法执行前进行事务的开启,执行后可以进行异常捕获以及决定提交还是回滚)。当检测到异常时completeTransactionAfterThrowing会进行处理,而顺利执行时,最终清除事务状态cleanupTransactionInfo并且进行事务提交commitTransactionAfterReturning。本文先不去展开每一步的具体实现,后文主要是通过DataSourceTransactionManager来说明这些方法都是如何具体执行的。

总结Spring事务处理的主干流程

1. 根据为事务配置的属性,创建事务 (相当于环绕通知的前置通知)
2. 责任链继续执行,最终执行目标方法
3. 捕获到异常时 执行提交还是回滚,但都抛出异常
4. 无论是否有异常最后清空事务状态
5. 无异常时执行提交

上一篇下一篇

猜你喜欢

热点阅读