SourceCode Java SpringBoot Spring Code

Spring事务源码简单分析

Posted on 2022-05-27,8 min read

寻找源码

  • 首先我们从@Transactional注解出发。

    image
  • 查看引用,可以看到有个名叫SpringTransactionAnnotationParser的类比较可疑,根据名字理解,作用就是用来解析@Transactional注解的。

  • 我们定位到这个方法,可以看到它实际上就是在获取注解上的属性。(下面的几个方法是根据方法引用链一步步往上找的)

    // SpringTransactionAnnotationParser#parseTransactionAnnotation
    protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
        RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    
        Propagation propagation = attributes.getEnum("propagation");
        rbta.setPropagationBehavior(propagation.value());
        Isolation isolation = attributes.getEnum("isolation");
        rbta.setIsolationLevel(isolation.value());
    
        rbta.setTimeout(attributes.getNumber("timeout").intValue());
        String timeoutString = attributes.getString("timeoutString");
        Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,
                      "Specify 'timeout' or 'timeoutString', not both");
        rbta.setTimeoutString(timeoutString);
    
        rbta.setReadOnly(attributes.getBoolean("readOnly"));
        rbta.setQualifier(attributes.getString("value"));
        rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
    
        List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
        for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
            rollbackRules.add(new RollbackRuleAttribute(rbRule));
        }
        for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
            rollbackRules.add(new RollbackRuleAttribute(rbRule));
        }
        for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
            rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
        }
        for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
            rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
        }
        rbta.setRollbackRules(rollbackRules);
    
        return rbta;
    }
    
    // SpringTransactionAnnotationParser#parseTransactionAnnotation
    public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
            element, Transactional.class, false, false);
        if (attributes != null) {
            return parseTransactionAnnotation(attributes);
        }
        else {
            return null;
        }
    }
    
    
    // AnnotationTransactionAttributeSource#determineTransactionAttribute
    @Nullable
    protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
        for (TransactionAnnotationParser parser : this.annotationParsers) {
            TransactionAttribute attr = parser.parseTransactionAnnotation(element);
            if (attr != null) {
                return attr;
            }
        }
        return null;
    }
    
    
  • 然后定位到了AnnotationTransactionAttributeSource这个类,里面有这两个方法。根据名称不难理解,一个是寻找类上的@Transactional注解,一个是寻找方法上的@Transactional注解。

    @Override
    @Nullable
    protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
        return determineTransactionAttribute(clazz);
    }
    
    @Override
    @Nullable
    protected TransactionAttribute findTransactionAttribute(Method method) {
        return determineTransactionAttribute(method);
    }
    
  • 然后这两个方法具体在AbstractFallbackTransactionAttributeSource这个类中被引用。下面是这个类的三个方法,注意下,这个类和上面的那个类AnnotationTransactionAttributeSource是有继承关系的,很明显,这里使用到了一个模板方法模式。

    @Nullable
    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        }
    
        // The method may be on an interface, but we need attributes from the target class.
        // If the target class is null, the method will be unchanged.
        Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    
        // First try is the method in the target class.
        TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
        if (txAttr != null) {
            return txAttr;
        }
    
        // Second try is the transaction attribute on the target class.
        txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }
    
        if (specificMethod != method) {
            // Fallback is to look at the original method.
            txAttr = findTransactionAttribute(method);
            if (txAttr != null) {
                return txAttr;
            }
            // Last fallback is the class of the original method.
            txAttr = findTransactionAttribute(method.getDeclaringClass());
            if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
                return txAttr;
            }
        }
    
        return null;
    }
    
    @Nullable
    protected abstract TransactionAttribute findTransactionAttribute(Class<?> clazz);
    
    /**
    	 * Subclasses need to implement this to return the transaction attribute for the
    	 * given method, if any.
    	 * @param method the method to retrieve the attribute for
    	 * @return all transaction attribute associated with this method, or {@code null} if none
    	 */
    @Nullable
    protected abstract TransactionAttribute findTransactionAttribute(Method method);
    
  • 再次寻找引用,可以定位到一个这个类中的AbstractFallbackTransactionAttributeSource#getTransactionAttribute方法。再次寻找这个方法的引用。可以到如下结果:

    image
  • 这时候,我们定位到了两个和切面相关的词语,Aspect和PointCut。那说明我们已经差不多找到切面了。

  • 最后,我们定位到了TransactionAspectSupport这个类,就是实现我们@Transactional功能的一个核心类的。然后我们开始源码的分析。

源码分析

  • TransactionAspectSupport这个类的重点方法在invokeWithinTransaction上。名字很直接了,在事务里执行。

  • 而这个方法又被TransactionInterceptor#invoke调用。

  • TransactionInterceptor的类签名如下:

    public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable{
        // other code
    }
    
  • 可以看到,它实现了一个MethodInterceptor接口,而这个接口就是Spring为我们提供的AOP接口。所以八股文常说@Transactional是基于AOP实现的,原因就在这儿。

  • 于是我们对重点方法TransactionAspectSupport#invokeWithinTransaction进行分析,以注释的形式。这里说白了就是根据不同的事务管理器类型,去执行不同的操作。

    @Nullable
    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();
        final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        // 根据注解上的配置来选择使用的事务管理器。
        final TransactionManager tm = determineTransactionManager(txAttr);
    
        if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
    		// 便于查看,忽略代码,位置1。
        }
    
        PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
        if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
    		// 便于查看,忽略代码,位置2。
        }
    
        else {
    		// 便于查看,忽略代码,位置3。
        }
    }
    
  • 由于不同版本的SB这个源码有点变化,本文基于:2.4.3,所以这里只分析位置2的代码。

    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 如果没有事务就创建事务,创建的事务,会从数据库连接池中获取连接,通过ThreadLoacl放入当前线程中,并设置connect.setAutoCommit= false
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        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) {
    		// 如果目标方法抛异常,那么进行回滚。主要根据TransactionAttribute.rollbackOn(Throwable)来判断是否回滚,默认实现应该是RuleBasedTransactionAttribute,也就是注解中设置的rollbackFor那些,当然也有其他处理,建议点开方法去看。
            // target invocation exception
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            cleanupTransactionInfo(txInfo);
        }
    
        if (retVal != null && 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;
    }
    

其他

  • 这里只对@Transactional的基本实现原理进行了源码分析,至于传播行为是怎么设置的,建议此处只给出方法位置,就不具体做分析了,建议自己打断点调。
    • 传播行为:处理位于AbstractPlatformTransactionManager#handleExistingTransaction,主要用于AbstractPlatformTransactionManager#getTransaction方法。

总结

  • 这里简单的对SB中@Transactional的源码进行了分析,可以明确的知道的是SB中的事务是基于 SB自带的MethodInterceptor来实现的,而MethodInterceptor又是SB中AOP的实现,所以要想事务生效,除了注意配置rollbackFor、隔离级别、传播行为等基本条件,还需要满足当前代理框架(JDK动态代理或CGLIB)的基本条件,才能保证事务生效(类或方法不能是final,方法不能是private啥的)。

下一篇: Spring AOP源码分析→

loading...
��