基于 @Transactional 的声明式事务原理剖析

开发 前端
@Transactional 注解的解析时机?既然大家都知道原理是通过 AOP 实现的,那么 Spring 何时解析的该注解并生成的代理对象呢?当我们业务代码执行到事务方法时,代理对象是如何介入并将事务管理起来的?

面试中经常会被问到:“为什么 Spring 通过一个注解就可以实现事务管理呢?”,一般大家听到这个问题都会回答:因为 Spring 是通过 AOP 实现的。当再被追问道“那你能详细说一下 @Transactional 的工作机制吗?它是如何通过 AOP 控制事务的?”的时候,很多小伙伴就开始支支吾吾,乱说一通了 😂。

在上一篇文章Spring 是如何管理事务的当中,我们讨论了有关于事务的一些基本概念以及 Spring 管理事务的两种方式——编程式和声明式,并对 @Transactional 注解的源码及简单使用进行了介绍。今天,我们书接上回,在源码的层面上对 @Transactional 注解的工作原理进行分析,希望通过下面的分析,让更多的小伙伴了解到 Spring 声明式事务的工作原理,让大家在面试时更从容的回答上述面试问题。


Tip:阅读本篇内容可能需要小伙伴们对 Spring Bean 的生命周期以及 Spring AOP 有些基本的了解。

好的,开始之前我们先明确一下 Spring 使用 @Transactional 注解管理事务两个重要内容,后边我们根据这两点来逐步揭开它背后的神秘:

  • @Transactional 注解的解析时机?既然大家都知道原理是通过 AOP 实现的,那么 Spring 何时解析的该注解并生成的代理对象呢?
  • 当我们业务代码执行到事务方法时,代理对象是如何介入并将事务管理起来的?

代理生成

在 Spring Bean 的生命周期中,创建 Spring Bean 时有几个重点方法:

  • createBeanInstance():构造实例化对象
  • populateBean():属性装配
  • initializeBean():Bean 的初始化

当一个 Spring Bean 被创建时,它会依次执行这些方法。在这个阶段,Spring 提供了非常多的扩展点来插手我们的 Bean 创建过程,而 Spring AOP 就是在执行到 initializeBean() 方法时,通过 BPP(BeanPostProcessor)Bean 后置处理器来实现的。

下面我们通过源码来逐步分析下(源码出自 spring-framework-5.3.33),源码中我们只分析与本文相关的核心逻辑:

在 initializeBean() 方法中,执行了 Bean 的初始化前处理和后处理,其中 AOP 是在后处理中完成的。

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            // 执行部分Aware方法
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    } else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 初始化前处理
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // 执行 Bean 的初始化
        invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        /**
         * 执行 Bean 的初始化后处理
         * AOP 配置方式 DefaultAdvisorAutoProxyCreator 在这里开始介入
         */
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

applyBeanPostProcessorsAfterInitialization() 方法中对 Bean 进行了后置处理,处理 AOP 的实现类为 DefaultAdvisorAutoProxyCreator,BPP 接口的方法实现最终会找到其父类 AbstractAutoProxyCreator。

AbstractAutoProxyCreator 中对接口方法的实现如下:

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 重点关注 wrapIfNecessary 方法
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

wrapIfNecessary() 这个方法的作用就是判断下当前的 Bean 是否需要生成代理,需要的话,生成一个代理对象返回。那么,我们被 @Transactional 注解标记的类肯定是要生成代理才能执行事务逻辑的,我们继续往下分析,看看何时解析的 @Transactional 注解。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey){
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 关键方法,这里查找是否有匹配的 Advisor,有就创建代理
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理对象
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    // 标记该类不需要加强,并返回普通的 bean 实例
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

上述逻辑在查找 Advisor,什么是 Advisor?

Advisor 接口是 Spring AOP 中的一个顶层抽象,用于将切点(Pointcut)和通知(Advice)组合关联起来。Advisor 接口的设计目的并不是为了提供给 Spring 用户使用的,而是为了支持不同类型的 Advice,它作为一种内部机制来确保不同类型的 Advice 能够有一致的支持方式。

实践中,我们更关注它的子接口 PointcutAdvisor。PointcutAdvisor 继承自 Advisor 接口并增加了 getPointcut() 方法,PointcutAdvisor 通过其持有的 Pointcut 来决定是否对某个连接点(方法调用)应用其 Advice。所以说,这个接口它确保了切点和通知之间的正确关联,它是 Advice + Pointcut 组合更好的诠释。

getAdvicesAndAdvisorsForBean() 方法是一个模板方法,由其子类 AbstractAdvisorAutoProxyCreator 实现,并调用了本类的 findEligibleAdvisors() 方法:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    /**
     * 此处方法调用不再深入,主要目的是获取当前容器所有的 advisors。
     * 通过 getBean() 方法,获取容器中所有 Advisor 接口类型的 Bean 的集合
     */
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 从获取到的 Advisor 集合中获取当前 Bean 对象适用的 Advisors
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 对advisor进行排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

findEligibleAdvisors() 方法通过 getBean() 获取了容器中所有 Advisor 类型的 Bean,然后内部调用 findAdvisorsThatCanApply() 方法从已获取到的 Advisor 集合中找到可以适配当前 Bean 对象的那些 Advisor,该方法内部最终是通过层层调用 AopUtils 的重载方法 canApply() 实现的:

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    } else if (advisor instanceof PointcutAdvisor) {
        // 处理事务的 Advisor 为 BeanFactoryTransactionAttributeSourceAdvisor
        // 该类的父类实现了 PointcutAdvisor 接口
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    } else {
        // It doesn't have a pointcut so we assume it applies.
        return true;
    }
}

执行到此处后,我们找到了处理事务的 Advisor 为 BeanFactoryTransactionAttributeSourceAdvisor,然后再次执行重载的 canApply() 方法,来判断给定的切点(pca.getPointcut())是否适用于给定的类(targetClass),这里通过 pca.getPointcut() 获取到的切点为 TransactionAttributeSourcePointcut:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // 首先通过类过滤器进行匹配,检查目标类是否符合要求
    if (!pc.getClassFilter().matches(targetClass)) {
        // 不符合,表示该 Pointcut 不能应用于此目标类
        return false;
    }

    // 这里获取切点上的方法级别的匹配器
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        return true;
    }

    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    // 创建一个 Set 集合来保存目标类和其所有接口
    Set<Class<?>> classes = new LinkedHashSet<>();
    // 如果目标类不是代理类,添加到集合
    if (!Proxy.isProxyClass(targetClass)) {
        classes.add(ClassUtils.getUserClass(targetClass));
    }
    // 添加目标类的所有接口
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

    // 遍历目标类和它的所有接口
    for (Class<?> clazz : classes) {
        // 通过反射获取当前类声明的所有方法(包括从父类继承的方法)
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if (introductionAwareMethodMatcher != null ?
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    // 使用方法匹配器进行逐个匹配
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    return false;
}

TransactionAttributeSourcePointcut 这个切点类实现了 MethodMatcher 接口并重写了 matches 方法,所以执行方法匹配时,会走到如下逻辑:

public boolean matches(Method method, Class<?> targetClass) {
    /**
     * TransactionAttributeSource 是一个获取事务属性的策略接口(事务属性为注解中的属性配置)
     * 这里获取到的接口实现为 AnnotationTransactionAttributeSource
     * 该类用于解析 @Transactional 注解,并根据注解中的配置(例如传播行为、隔离级别等)
     * 构建相应的 TransactionAttribute 对象
     */
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

调用 AnnotationTransactionAttributeSource 的 getTransactionAttribute() 方法来解析事务属性,此方法由其父类 AbstractFallbackTransactionAttributeSource 实现,该方法逻辑比较简单,内部主要是通过调用本类的 computeTransactionAttribute() 方法,我们直接来看下后边这个方法:

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // 非 public 修饰的直接返回 null,返回 null 意味着方法匹配器没有匹配成功,也就是该切面不会应用到这个 Bean,也就不会生成代理
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }

    // 这里的主要目的是找到目标类标记事务的具体实现方法,确保事务配置被正确解析
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

    // 首先尝试从方法上查找事务属性
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
        return txAttr;
    }

    // 如果没有在方法上查找到事务属性,尝试从声明该方法的类中查找。
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
    }

    /**
     * 如果找到的具体实现方法与原始方法对象不同,说明已经尝试过更具体的版本,但未能找到事务属性。
     * 因此,这里会再次尝试使用原始方法及其声明类来查找事务属性。
     */
    if (specificMethod != method) {
        // 使用原始方法查找
        txAttr = findTransactionAttribute(method);
        if (txAttr != null) {
            return txAttr;
        }
        // 使用原始方法的声明类查找
        txAttr = findTransactionAttribute(method.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }
    }
    // 如果经过上述所有步骤后仍未找到事务属性,则返回null,表示该方法不支持事务管理
    return null;
}

通过理解这段代码的工作机制,可以看出:

  • 非 public 方法是不会生成代理的,所以这里解释了为什么 @Transactional 注解加在非public 方法事务会失效的原因;
  • 同时,从查找事务属性的执行逻辑来看,这段代码也证明了 @Transactional 注解方法级别的配置优先于类级别的配置。

上述逻辑中的 findTransactionAttribute() 方法也是一个模板方法,这里会调用至 AnnotationTransactionAttributeSource 类中,这个方法有两个重载版本,分别用来处理方法和类级别的注解属性解析,它们方法内部都调用了同一个方法来进行属性解析:

protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
    return determineTransactionAttribute(clazz);
}

protected TransactionAttribute findTransactionAttribute(Method method) {
    return determineTransactionAttribute(method);
}

protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    for (TransactionAnnotationParser parser : this.annotationParsers) {
        // 遍历解析器进行注解解析,有一个解析器解析成功便直接返回
        TransactionAttribute attr = parser.parseTransactionAnnotation(element);
        if (attr != null) {
            return attr;
        }
    }
    return null;
}

determineTransactionAttribute() 方法中开始遍历解析器对事务注解进行解析,解析器有 3 种:

  • Ejb3TransactionAnnotationParser:用于解析 EJB 3 标准中的 javax.ejb.TransactionAttribute 注解。
  • JtaTransactionAnnotationParser:用于解析 JTA 1.2 规范下的 javax.transaction.Transactional 注解。
  • SpringTransactionAnnotationParser:用于解析 Spring 框架的 @Transactional 注解。

这里解析 @Transactional 注解的解析器为 SpringTransactionAnnotationParser。我们继续看下 SpringTransactionAnnotationParser 中 parseTransactionAnnotation() 方法的解析逻辑:

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    // 从目标元素上获取指定类型的注解属性,这里注解是 Transactional
    AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
            element, Transactional.class, false, false);
    if (attributes != null) {
        // 获取到调用重载方法执行解析
        return parseTransactionAnnotation(attributes);
    }
    else {
        return null;
    }
}

parseTransactionAnnotation() 方法中又调用了其重载方法,执行具体的属性解析逻辑,下面我们看下这段解析代码的逻辑,相信大家看到这里应该会非常熟悉,这里解析的就是我们在 @Transactional 注解中配置的属性:

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

好了,到这里,判断 BeanFactoryTransactionAttributeSourceAdvisor 这个 Advisor 能否应用到当前 Bean 的逻辑就完成了,如果能够成功解析到事务属性返回值就不为 null,那么方法匹配器就会返回 true,canApply() 方法也就返回了 true,也就代表这个 Advisor 是可以应用到当前 Bean 的,接下来就可以创建代理了。

我们可以回到上面 AbstractAutoProxyCreator 类的 wrapIfNecessary() 方法中看下,如果 getAdvicesAndAdvisorsForBean() 返回不为空,那么就要为当前 Bean 创建代理,执行 createProxy() 方法,方法逻辑如下:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
   @Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    /**
     * 下边这块逻辑主要是在判断代理要基于类代理还是基于接口进行代理
     * 首先,检查是否设置了 proxyTargetClass = true 属性(即 CGLIB 代理),这个属性的两种设置方式:
     * 注解方式:@EnableAspectJAutoProxy(proxyTargetClass = true)
     * xml 配置方式:<aop:aspectj-autoproxy proxy-target-class="true" />
     */
    if (proxyFactory.isProxyTargetClass()) {
        // Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
        if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
            // 在创建新的代理时,也要将该类实现的所有接口包含进来,代理类要保持类的原有行为
            for (Class<?> ifc : beanClass.getInterfaces()) {
                proxyFactory.addInterface(ifc);
            }
        }
    }
    // 如果开发时没有明确要求使用 CGLIB 代理,也就是要走接口代理(JDK 代理)
    else {
        /**
         * 检查是否设置了 preserveTargetClass 属性,这个属性可通过 BeanDefinition 进行设置
         * 如果 BeanDefinition 中设置了该属性,那么这里要使用 CGLIB 代理
         * Tip:熟悉 Bean 生命周期的小伙伴儿们可能知道,我们可以通过 BeanFactoryPostProcessor 来
         * 干预 Bean 实例化过程,调整 Bean 的元数据,也就是 BeanDefinition
         */
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            /**
             * 评估是否使用接口代理
             * 检查目标类有符合要求的接口:
             * 如果有,则将目标类的接口设置到代理工厂的 interfaces 属性,执行 JDK 动态代理
             * 如果没有,则设置 proxyTargetClass = true,执行 CGLIB 代理
             */
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 将之前查找到的 Advisor 设置到代理工厂
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    // 可以通过继承该类来自定义代理工厂,这里是预留的模板方法
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // Use original ClassLoader if bean class not locally loaded in overriding class loader
    ClassLoader classLoader = getProxyClassLoader();
    if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
        classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
    }

    // 生成代理对象并返回
    return proxyFactory.getProxy(classLoader);
}

通过上面源码分析得知,createProxy() 方法的主要任务是:

  • 创建代理工厂,并根据用户配置和类的接口信息等来判断应该使用 CGLIB 代理还是 JDK 代理来创建代理对象;
  • 代理工厂使用已设置好的属性参数来生成动态代理对象并返回。

再往后面就是使用具体的 AOP 代理工厂生成代理对象的逻辑了,这里我们不再继续跟踪源码了,感兴趣的小伙伴儿们可以自己再往后跟踪分析一下。

其实,执行到这里的话,我们就知道了,在 Bean 的初始化过程中,Spring 已经对包含 @Transactional 注解的 Bean 进行了解析并生成了代理对象存储到了容器中。

事务管理

既然代理对象已经生成了,那么熟悉代理模式的小伙伴儿就知道了,我们在执行目标方法的时候就会导向到代理对象中定义的行为中去,具体如下:

  • CGLIB 代理:会导向到 MethodInterceptor 接口的 intercept() 方法中
  • JDK 代理:会导向到 InvocationHandler 接口的 invoke() 方法中

上述创建代理逻辑中通过 proxyFactory.getProxy(classLoader); 去获取代理对象时,实际生产代理对象的代理类有两个默认实现 CglibAopProxy 与 JdkDynamicAopProxy,具体执行哪个类去生产代理对象也是通过 proxyTargetClass 属性与目标类是否实现接口来判断的。下边我们以 JdkDynamicAopProxy 这个代理工厂为例看下 invoke() 的核心执行逻辑:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        // ......

        // 如果配置了 expose-proxy 属性为 true,通过 ThreadLocal 保存到上下文中
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // 获取针对此方法的拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        if (chain.isEmpty()) {
            // 拦截器链为空,直接通过反射调用目标方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 创建一个包含代理对象、目标对象、方法、参数等信息的 MethodInvocation 实例
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 通过拦截器链执行方法调用
            retVal = invocation.proceed();
        }

        return retVal;
      }
}

在 invoke() 方法的逻辑中,我们可以看到其核心步骤是获取适用于目标方法的拦截器链,并利用这个拦截器链作为参数来实例化一个 ReflectiveMethodInvocation 对象。随后,通过调用该对象的 proceed() 方法,依次执行拦截器链中的每个拦截器,执行到最后调用我们的目标方法,后面我们具体来分析这个执行过程。

ReflectiveMethodInvocation 是 Spring AOP 内部用于表示一次具体方法调用的对象,它封装了实际的目标方法调用,并且负责管理拦截器链的执行流程。

我们首先来看下获取拦截器链的逻辑,getInterceptorsAndDynamicInterceptionAdvice() 方法最终调用至 DefaultAdvisorChainFactory 类的同名方法中:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    Advisor[] advisors = config.getAdvisors();
    List<Object> interceptorList = new ArrayList<>(advisors.length);
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    Boolean hasIntroductions = null;

    for (Advisor advisor : advisors) {
        if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // 先看类与切点是否匹配
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                // 获取切点的方法匹配器
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                boolean match;
                if (mm instanceof IntroductionAwareMethodMatcher) {
                    if (hasIntroductions == null) {
                        hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                    }
                    match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
                }
                else {
                    // 使用方法匹配器进行方法匹配
                    match = mm.matches(method, actualClass);
                }
                if (match) {
                    // 匹配成功后,通过适配器将 Advisor 转换成 MethodInterceptor
                    // 并将所有拦截器加入到拦截器链中
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    if (mm.isRuntime()) {
                        /**
                         * 如果是运行时匹配,则创建一个新的动态方法匹配器实例并将其加入到拦截器链中
                         * 这个对象包含 MethodInterceptor 和 MethodMatcher 的实例
                         */
                        for (MethodInterceptor interceptor : interceptors) {
                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                        }
                    }
                    else {
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }
            }
        }
        // ......
    // 返回构建好的拦截器链
    return interceptorList;
}

getInterceptorsAndDynamicInterceptionAdvice() 方法负责构建适用于特定方法调用的拦截器链。该方法的主要任务是从 IOC 容器中获取所有的 Advisor 对象,并根据目标对象及其方法进行筛选和适配,最终返回一个实现或者封装了 MethodInterceptor 接口的对象组成的列表。

再来看下 proceed() 方法的逻辑:

public Object proceed() throws Throwable {
    /**
     * 索引变量,用来跟踪当前正在处理的拦截器位置。每次调用 proceed() 方法时,它都会增加,指向下一个要执行的拦截器。
     * 这里从索引为 -1 的拦截器开始调用按序递增,并检查是否已经到达拦截器链的末尾
     * 如果当前索引已经是最后一个拦截器,则通过反射直接调用目标方法
     */
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 当拦截器链中的所有拦截器都已经被处理完毕后,调用此方法来执行实际的目标方法(即业务逻辑方法)。
        // 这标志着拦截器链的结束
        return invokeJoinpoint();
    }

    // 获取下一个要执行的拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 如果是动态切点拦截器,需要在运行时评估其是否应该激活
        // 静态匹配部分已经在构建拦截器链时完成,并且匹配成功
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 使用方法匹配器匹配,判断是否应用此拦截器
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {

            // 匹配成功,则调用该拦截器的 invoke 方法,继续执行拦截器逻辑
            return dm.interceptor.invoke(this);
        }
        else {
            // 匹配失败,跳过当前拦截器,递归调用 proceed() 继续下一个拦截器
            return proceed();
        }
    }
    else {
        // 直接调用 MethodInterceptor 的 invoke 方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

proceed() 方法的作用就是按顺序逐个执行拦截器链中的拦截器。它从索引 -1 开始,逐个执行拦截器,直至执行到拦截器链的末尾后,完成对目标方法的调用。

好的,分析了这么多,我们的代理对象到底是如何将事务管理起来的呢?

事务的实现也是通过拦截器来实现的,这个拦截器是 TransactionInterceptor,它实现了 MethodInterceptor接口。那么,当目标方法为事务方法时,proceed() 方法中调用的拦截器的 invoke() 方法会调用到 TransactionInterceptor 类中:

public Object invoke(MethodInvocation invocation) throws Throwable {
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // 调用内部方法 invokeWithinTransaction 来处理事务逻辑
    // 回调接口用于提供具体的执行逻辑,包括如何继续执行拦截器链或目标方法,以及访问目标对象和方法参数等
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
        @Override
        @Nullable
        public Object proceedWithInvocation() throws Throwable {
            // 继续执行拦截器链中的下一个元素
            return invocation.proceed();
        }
        @Override
        public Object getTarget() {
            // 返回当前调用的目标对象实例
            return invocation.getThis();
        }
        @Override
        public Object[] getArguments() {
            // 返回传递给目标方法的参数数组
            return invocation.getArguments();
        }
    });
}

可以看出,该方法处理带有事务管理的目标方法调用主要是通过内部方法 invokeWithinTransaction() 实现的,这个方法会调用至其父类 TransactionAspectSupport 类中:

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
   final InvocationCallback invocation) throws Throwable {

    // 如果没有事务属性,则该方法是非事务性的
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 计算事务属性
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    // 根据属性配置获取事务管理器,没有配置获取默认事务管理器
    final TransactionManager tm = determineTransactionManager(txAttr);

    // ......

    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { // 声明式事务处理
        // 开启事务,将事务信息封装成 TransactionInfo 对象
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

        Object retVal;
        try {
            // 环绕通知,执行目标方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            /**
             * 处理目标方法调用期间抛出的异常
             * 根据在 rollbackFor 中指定的回滚的异常类型匹配,决定事务回滚或者提交
             */
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // 清理事务信息
            cleanupTransactionInfo(txInfo);
        }

        // ......
        // 在正常返回后提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    } else { // 编程式事务处理
        Object result;
        final ThrowableHolder throwableHolder = new ThrowableHolder();
        try {
            result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
                try {
                    Object retVal = invocation.proceedWithInvocation();
                    if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
                        retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                    }
                    return retVal;
                }
                catch (Throwable ex) {
                    // 根据事务属性判断是否需要回滚
                    if (txAttr.rollbackOn(ex)) {
                        // 如果是运行时异常,直接抛出
                        if (ex instanceof RuntimeException) {
                            throw (RuntimeException) ex;
                        }
                        else {
                            throw new ThrowableHolderException(ex);
                        }
                    }
                    else {
                        // 非回滚异常,记录并返回 null
                        throwableHolder.throwable = ex;
                        return null;
                    }
                }
                finally {
                    // 清理事务信息
                    cleanupTransactionInfo(txInfo);
                }
            });
        }
        catch (ThrowableHolderException ex) {
            throw ex.getCause();
        }

        return result;
    }
}

篇幅原因,我们就不再继续深究事务的控制细节了。其实分析到这里的话,我们也基本了解了当一个带有 @Transactional 注解的方法被调用时,声明式事务究竟是如何被控制的。

那就是,Spring AOP 机制会使用代理对象来拦截事务方法的执行。最终通过 invokeWithinTransaction() 方法确保目标方法在一个适当的事务上下文中运行。它根据 @Transactional 中配置的事务属性选择合适的事务管理器,并决定是否需要创建或加入现有事务。然后,在事务边界控制逻辑中,代理对象会在执行目标方法前开启事务,在方法正常结束时提交事务;如果过程中抛出了异常,则根据事务属性配置的规则进行回滚。整个过程自动完成了事务的开启、提交和回滚。

总结

为了方便理解上述整个过程,请看下图:

图片图片

责任编辑:武晓燕 来源: Java驿站
相关推荐

2009-06-22 09:01:57

Spring声明式事务

2023-05-05 07:39:04

Spring事务面试

2024-11-13 19:03:14

2022-06-17 08:37:14

分布式事务分库分表

2024-06-28 09:07:19

2021-09-06 13:42:14

Spring声明式事务

2023-05-12 08:02:43

分布式事务应用

2024-01-26 13:17:00

rollbackMQ订单系统

2009-02-11 13:08:29

事务提交事务管理Spring

2009-02-11 11:14:31

事务管理事务开始Spring

2009-06-22 11:01:12

2019-11-19 08:32:26

数据库HLC事务

2023-11-02 07:52:30

Java工具

2022-06-21 08:27:22

Seata分布式事务

2023-09-27 16:22:51

SpringMySQL原子性

2021-06-26 14:59:13

SpringTransaction执行

2023-09-28 09:07:54

注解失效场景

2017-04-20 12:30:57

声明式爬虫网络

2009-06-22 14:59:51

AOP实现原理声明式编程命令式编程

2021-04-15 08:01:27

Spring声明式事务
点赞
收藏

51CTO技术栈公众号