自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于 @Transactional 的聲明式事務(wù)原理剖析

開發(fā) 前端
@Transactional?注解的解析時機(jī)?既然大家都知道原理是通過 AOP 實(shí)現(xiàn)的,那么 Spring 何時解析的該注解并生成的代理對象呢?當(dāng)我們業(yè)務(wù)代碼執(zhí)行到事務(wù)方法時,代理對象是如何介入并將事務(wù)管理起來的?

面試中經(jīng)常會被問到:“為什么 Spring 通過一個注解就可以實(shí)現(xiàn)事務(wù)管理呢?”,一般大家聽到這個問題都會回答:因?yàn)?Spring 是通過 AOP 實(shí)現(xiàn)的。當(dāng)再被追問道“那你能詳細(xì)說一下 @Transactional 的工作機(jī)制嗎?它是如何通過 AOP 控制事務(wù)的?”的時候,很多小伙伴就開始支支吾吾,亂說一通了 ??。

在上一篇文章Spring 是如何管理事務(wù)的當(dāng)中,我們討論了有關(guān)于事務(wù)的一些基本概念以及 Spring 管理事務(wù)的兩種方式——編程式和聲明式,并對 @Transactional 注解的源碼及簡單使用進(jìn)行了介紹。今天,我們書接上回,在源碼的層面上對 @Transactional 注解的工作原理進(jìn)行分析,希望通過下面的分析,讓更多的小伙伴了解到 Spring 聲明式事務(wù)的工作原理,讓大家在面試時更從容的回答上述面試問題。


Tip:閱讀本篇內(nèi)容可能需要小伙伴們對 Spring Bean 的生命周期以及 Spring AOP 有些基本的了解。

好的,開始之前我們先明確一下 Spring 使用 @Transactional 注解管理事務(wù)兩個重要內(nèi)容,后邊我們根據(jù)這兩點(diǎn)來逐步揭開它背后的神秘:

  • @Transactional 注解的解析時機(jī)?既然大家都知道原理是通過 AOP 實(shí)現(xiàn)的,那么 Spring 何時解析的該注解并生成的代理對象呢?
  • 當(dāng)我們業(yè)務(wù)代碼執(zhí)行到事務(wù)方法時,代理對象是如何介入并將事務(wù)管理起來的?

代理生成

在 Spring Bean 的生命周期中,創(chuàng)建 Spring Bean 時有幾個重點(diǎn)方法:

  • createBeanInstance():構(gòu)造實(shí)例化對象
  • populateBean():屬性裝配
  • initializeBean():Bean 的初始化

當(dāng)一個 Spring Bean 被創(chuàng)建時,它會依次執(zhí)行這些方法。在這個階段,Spring 提供了非常多的擴(kuò)展點(diǎn)來插手我們的 Bean 創(chuàng)建過程,而 Spring AOP 就是在執(zhí)行到 initializeBean() 方法時,通過 BPP(BeanPostProcessor)Bean 后置處理器來實(shí)現(xiàn)的。

下面我們通過源碼來逐步分析下(源碼出自 spring-framework-5.3.33),源碼中我們只分析與本文相關(guān)的核心邏輯:

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

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

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

    try {
        // 執(zhí)行 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()) {
        /**
         * 執(zhí)行 Bean 的初始化后處理
         * AOP 配置方式 DefaultAdvisorAutoProxyCreator 在這里開始介入
         */
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

applyBeanPostProcessorsAfterInitialization() 方法中對 Bean 進(jìn)行了后置處理,處理 AOP 的實(shí)現(xiàn)類為 DefaultAdvisorAutoProxyCreator,BPP 接口的方法實(shí)現(xiàn)最終會找到其父類 AbstractAutoProxyCreator。

AbstractAutoProxyCreator 中對接口方法的實(shí)現(xiàn)如下:

@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) {
            // 重點(diǎn)關(guān)注 wrapIfNecessary 方法
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

wrapIfNecessary() 這個方法的作用就是判斷下當(dāng)前的 Bean 是否需要生成代理,需要的話,生成一個代理對象返回。那么,我們被 @Transactional 注解標(biāo)記的類肯定是要生成代理才能執(zhí)行事務(wù)邏輯的,我們繼續(xù)往下分析,看看何時解析的 @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;
    }

    // 關(guān)鍵方法,這里查找是否有匹配的 Advisor,有就創(chuàng)建代理
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 創(chuàng)建代理對象
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    // 標(biāo)記該類不需要加強(qiáng),并返回普通的 bean 實(shí)例
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

上述邏輯在查找 Advisor,什么是 Advisor?

Advisor 接口是 Spring AOP 中的一個頂層抽象,用于將切點(diǎn)(Pointcut)和通知(Advice)組合關(guān)聯(lián)起來。Advisor 接口的設(shè)計(jì)目的并不是為了提供給 Spring 用戶使用的,而是為了支持不同類型的 Advice,它作為一種內(nèi)部機(jī)制來確保不同類型的 Advice 能夠有一致的支持方式。

實(shí)踐中,我們更關(guān)注它的子接口 PointcutAdvisor。PointcutAdvisor 繼承自 Advisor 接口并增加了 getPointcut() 方法,PointcutAdvisor 通過其持有的 Pointcut 來決定是否對某個連接點(diǎn)(方法調(diào)用)應(yīng)用其 Advice。所以說,這個接口它確保了切點(diǎn)和通知之間的正確關(guān)聯(lián),它是 Advice + Pointcut 組合更好的詮釋。

getAdvicesAndAdvisorsForBean() 方法是一個模板方法,由其子類 AbstractAdvisorAutoProxyCreator 實(shí)現(xiàn),并調(diào)用了本類的 findEligibleAdvisors() 方法:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    /**
     * 此處方法調(diào)用不再深入,主要目的是獲取當(dāng)前容器所有的 advisors。
     * 通過 getBean() 方法,獲取容器中所有 Advisor 接口類型的 Bean 的集合
     */
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 從獲取到的 Advisor 集合中獲取當(dāng)前 Bean 對象適用的 Advisors
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 對advisor進(jìn)行排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

findEligibleAdvisors() 方法通過 getBean() 獲取了容器中所有 Advisor 類型的 Bean,然后內(nèi)部調(diào)用 findAdvisorsThatCanApply() 方法從已獲取到的 Advisor 集合中找到可以適配當(dāng)前 Bean 對象的那些 Advisor,該方法內(nèi)部最終是通過層層調(diào)用 AopUtils 的重載方法 canApply() 實(shí)現(xiàn)的:

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) {
        // 處理事務(wù)的 Advisor 為 BeanFactoryTransactionAttributeSourceAdvisor
        // 該類的父類實(shí)現(xiàn)了 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;
    }
}

執(zhí)行到此處后,我們找到了處理事務(wù)的 Advisor 為 BeanFactoryTransactionAttributeSourceAdvisor,然后再次執(zhí)行重載的 canApply() 方法,來判斷給定的切點(diǎn)(pca.getPointcut())是否適用于給定的類(targetClass),這里通過 pca.getPointcut() 獲取到的切點(diǎn)為 TransactionAttributeSourcePointcut:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // 首先通過類過濾器進(jìn)行匹配,檢查目標(biāo)類是否符合要求
    if (!pc.getClassFilter().matches(targetClass)) {
        // 不符合,表示該 Pointcut 不能應(yīng)用于此目標(biāo)類
        return false;
    }

    // 這里獲取切點(diǎn)上的方法級別的匹配器
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        return true;
    }

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

    // 創(chuàng)建一個 Set 集合來保存目標(biāo)類和其所有接口
    Set<Class<?>> classes = new LinkedHashSet<>();
    // 如果目標(biāo)類不是代理類,添加到集合
    if (!Proxy.isProxyClass(targetClass)) {
        classes.add(ClassUtils.getUserClass(targetClass));
    }
    // 添加目標(biāo)類的所有接口
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

    // 遍歷目標(biāo)類和它的所有接口
    for (Class<?> clazz : classes) {
        // 通過反射獲取當(dāng)前類聲明的所有方法(包括從父類繼承的方法)
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if (introductionAwareMethodMatcher != null ?
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    // 使用方法匹配器進(jìn)行逐個匹配
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    return false;
}

TransactionAttributeSourcePointcut 這個切點(diǎn)類實(shí)現(xiàn)了 MethodMatcher 接口并重寫了 matches 方法,所以執(zhí)行方法匹配時,會走到如下邏輯:

public boolean matches(Method method, Class<?> targetClass) {
    /**
     * TransactionAttributeSource 是一個獲取事務(wù)屬性的策略接口(事務(wù)屬性為注解中的屬性配置)
     * 這里獲取到的接口實(shí)現(xiàn)為 AnnotationTransactionAttributeSource
     * 該類用于解析 @Transactional 注解,并根據(jù)注解中的配置(例如傳播行為、隔離級別等)
     * 構(gòu)建相應(yīng)的 TransactionAttribute 對象
     */
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

調(diào)用 AnnotationTransactionAttributeSource 的 getTransactionAttribute() 方法來解析事務(wù)屬性,此方法由其父類 AbstractFallbackTransactionAttributeSource 實(shí)現(xiàn),該方法邏輯比較簡單,內(nèi)部主要是通過調(diào)用本類的 computeTransactionAttribute() 方法,我們直接來看下后邊這個方法:

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // 非 public 修飾的直接返回 null,返回 null 意味著方法匹配器沒有匹配成功,也就是該切面不會應(yīng)用到這個 Bean,也就不會生成代理
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }

    // 這里的主要目的是找到目標(biāo)類標(biāo)記事務(wù)的具體實(shí)現(xiàn)方法,確保事務(wù)配置被正確解析
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

    // 首先嘗試從方法上查找事務(wù)屬性
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
        return txAttr;
    }

    // 如果沒有在方法上查找到事務(wù)屬性,嘗試從聲明該方法的類中查找。
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
    }

    /**
     * 如果找到的具體實(shí)現(xiàn)方法與原始方法對象不同,說明已經(jīng)嘗試過更具體的版本,但未能找到事務(wù)屬性。
     * 因此,這里會再次嘗試使用原始方法及其聲明類來查找事務(wù)屬性。
     */
    if (specificMethod != method) {
        // 使用原始方法查找
        txAttr = findTransactionAttribute(method);
        if (txAttr != null) {
            return txAttr;
        }
        // 使用原始方法的聲明類查找
        txAttr = findTransactionAttribute(method.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }
    }
    // 如果經(jīng)過上述所有步驟后仍未找到事務(wù)屬性,則返回null,表示該方法不支持事務(wù)管理
    return null;
}

通過理解這段代碼的工作機(jī)制,可以看出:

  • 非 public 方法是不會生成代理的,所以這里解釋了為什么 @Transactional 注解加在非public 方法事務(wù)會失效的原因;
  • 同時,從查找事務(wù)屬性的執(zhí)行邏輯來看,這段代碼也證明了 @Transactional 注解方法級別的配置優(yōu)先于類級別的配置。

上述邏輯中的 findTransactionAttribute() 方法也是一個模板方法,這里會調(diào)用至 AnnotationTransactionAttributeSource 類中,這個方法有兩個重載版本,分別用來處理方法和類級別的注解屬性解析,它們方法內(nèi)部都調(diào)用了同一個方法來進(jìn)行屬性解析:

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) {
        // 遍歷解析器進(jìn)行注解解析,有一個解析器解析成功便直接返回
        TransactionAttribute attr = parser.parseTransactionAnnotation(element);
        if (attr != null) {
            return attr;
        }
    }
    return null;
}

determineTransactionAttribute() 方法中開始遍歷解析器對事務(wù)注解進(jìn)行解析,解析器有 3 種:

  • Ejb3TransactionAnnotationParser:用于解析 EJB 3 標(biāo)準(zhǔn)中的 javax.ejb.TransactionAttribute 注解。
  • JtaTransactionAnnotationParser:用于解析 JTA 1.2 規(guī)范下的 javax.transaction.Transactional 注解。
  • SpringTransactionAnnotationParser:用于解析 Spring 框架的 @Transactional 注解。

這里解析 @Transactional 注解的解析器為 SpringTransactionAnnotationParser。我們繼續(xù)看下 SpringTransactionAnnotationParser 中 parseTransactionAnnotation() 方法的解析邏輯:

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    // 從目標(biāo)元素上獲取指定類型的注解屬性,這里注解是 Transactional
    AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
            element, Transactional.class, false, false);
    if (attributes != null) {
        // 獲取到調(diào)用重載方法執(zhí)行解析
        return parseTransactionAnnotation(attributes);
    }
    else {
        return null;
    }
}

parseTransactionAnnotation() 方法中又調(diào)用了其重載方法,執(zhí)行具體的屬性解析邏輯,下面我們看下這段解析代碼的邏輯,相信大家看到這里應(yīng)該會非常熟悉,這里解析的就是我們在 @Transactional 注解中配置的屬性:

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();

    Propagation propagation = attributes.getEnum("propagation"); // 事務(wù)傳播方式
    rbta.setPropagationBehavior(propagation.value());
    Isolation isolation = attributes.getEnum("isolation");  // 事務(wù)隔離級別
    rbta.setIsolationLevel(isolation.value());

    rbta.setTimeout(attributes.getNumber("timeout").intValue());  // 事務(wù)超時時間
    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"));  // 只讀事務(wù)
    rbta.setQualifier(attributes.getString("value"));  // 事務(wù)管理器
    rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));

    // 回滾相關(guān)的設(shè)置
    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 能否應(yīng)用到當(dāng)前 Bean 的邏輯就完成了,如果能夠成功解析到事務(wù)屬性返回值就不為 null,那么方法匹配器就會返回 true,canApply() 方法也就返回了 true,也就代表這個 Advisor 是可以應(yīng)用到當(dāng)前 Bean 的,接下來就可以創(chuàng)建代理了。

我們可以回到上面 AbstractAutoProxyCreator 類的 wrapIfNecessary() 方法中看下,如果 getAdvicesAndAdvisorsForBean() 返回不為空,那么就要為當(dāng)前 Bean 創(chuàng)建代理,執(zhí)行 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);

    /**
     * 下邊這塊邏輯主要是在判斷代理要基于類代理還是基于接口進(jìn)行代理
     * 首先,檢查是否設(shè)置了 proxyTargetClass = true 屬性(即 CGLIB 代理),這個屬性的兩種設(shè)置方式:
     * 注解方式:@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)) {
            // 在創(chuàng)建新的代理時,也要將該類實(shí)現(xiàn)的所有接口包含進(jìn)來,代理類要保持類的原有行為
            for (Class<?> ifc : beanClass.getInterfaces()) {
                proxyFactory.addInterface(ifc);
            }
        }
    }
    // 如果開發(fā)時沒有明確要求使用 CGLIB 代理,也就是要走接口代理(JDK 代理)
    else {
        /**
         * 檢查是否設(shè)置了 preserveTargetClass 屬性,這個屬性可通過 BeanDefinition 進(jìn)行設(shè)置
         * 如果 BeanDefinition 中設(shè)置了該屬性,那么這里要使用 CGLIB 代理
         * Tip:熟悉 Bean 生命周期的小伙伴兒們可能知道,我們可以通過 BeanFactoryPostProcessor 來
         * 干預(yù) Bean 實(shí)例化過程,調(diào)整 Bean 的元數(shù)據(jù),也就是 BeanDefinition
         */
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            /**
             * 評估是否使用接口代理
             * 檢查目標(biāo)類有符合要求的接口:
             * 如果有,則將目標(biāo)類的接口設(shè)置到代理工廠的 interfaces 屬性,執(zhí)行 JDK 動態(tài)代理
             * 如果沒有,則設(shè)置 proxyTargetClass = true,執(zhí)行 CGLIB 代理
             */
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 將之前查找到的 Advisor 設(shè)置到代理工廠
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    // 可以通過繼承該類來自定義代理工廠,這里是預(yù)留的模板方法
    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() 方法的主要任務(wù)是:

  • 創(chuàng)建代理工廠,并根據(jù)用戶配置和類的接口信息等來判斷應(yīng)該使用 CGLIB 代理還是 JDK 代理來創(chuàng)建代理對象;
  • 代理工廠使用已設(shè)置好的屬性參數(shù)來生成動態(tài)代理對象并返回。

再往后面就是使用具體的 AOP 代理工廠生成代理對象的邏輯了,這里我們不再繼續(xù)跟蹤源碼了,感興趣的小伙伴兒們可以自己再往后跟蹤分析一下。

其實(shí),執(zhí)行到這里的話,我們就知道了,在 Bean 的初始化過程中,Spring 已經(jīng)對包含 @Transactional 注解的 Bean 進(jìn)行了解析并生成了代理對象存儲到了容器中。

事務(wù)管理

既然代理對象已經(jīng)生成了,那么熟悉代理模式的小伙伴兒就知道了,我們在執(zhí)行目標(biāo)方法的時候就會導(dǎo)向到代理對象中定義的行為中去,具體如下:

  • CGLIB 代理:會導(dǎo)向到 MethodInterceptor 接口的 intercept() 方法中
  • JDK 代理:會導(dǎo)向到 InvocationHandler 接口的 invoke() 方法中

上述創(chuàng)建代理邏輯中通過 proxyFactory.getProxy(classLoader); 去獲取代理對象時,實(shí)際生產(chǎn)代理對象的代理類有兩個默認(rèn)實(shí)現(xiàn) CglibAopProxy 與 JdkDynamicAopProxy,具體執(zhí)行哪個類去生產(chǎn)代理對象也是通過 proxyTargetClass 屬性與目標(biāo)類是否實(shí)現(xiàn)接口來判斷的。下邊我們以 JdkDynamicAopProxy 這個代理工廠為例看下 invoke() 的核心執(zhí)行邏輯:

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()) {
            // 攔截器鏈為空,直接通過反射調(diào)用目標(biāo)方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 創(chuàng)建一個包含代理對象、目標(biāo)對象、方法、參數(shù)等信息的 MethodInvocation 實(shí)例
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 通過攔截器鏈執(zhí)行方法調(diào)用
            retVal = invocation.proceed();
        }

        return retVal;
      }
}

在 invoke() 方法的邏輯中,我們可以看到其核心步驟是獲取適用于目標(biāo)方法的攔截器鏈,并利用這個攔截器鏈作為參數(shù)來實(shí)例化一個 ReflectiveMethodInvocation 對象。隨后,通過調(diào)用該對象的 proceed() 方法,依次執(zhí)行攔截器鏈中的每個攔截器,執(zhí)行到最后調(diào)用我們的目標(biāo)方法,后面我們具體來分析這個執(zhí)行過程。

ReflectiveMethodInvocation 是 Spring AOP 內(nèi)部用于表示一次具體方法調(diào)用的對象,它封裝了實(shí)際的目標(biāo)方法調(diào)用,并且負(fù)責(zé)管理攔截器鏈的執(zhí)行流程。

我們首先來看下獲取攔截器鏈的邏輯,getInterceptorsAndDynamicInterceptionAdvice() 方法最終調(diào)用至 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;
            // 先看類與切點(diǎn)是否匹配
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                // 獲取切點(diǎn)的方法匹配器
                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 {
                    // 使用方法匹配器進(jìn)行方法匹配
                    match = mm.matches(method, actualClass);
                }
                if (match) {
                    // 匹配成功后,通過適配器將 Advisor 轉(zhuǎn)換成 MethodInterceptor
                    // 并將所有攔截器加入到攔截器鏈中
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    if (mm.isRuntime()) {
                        /**
                         * 如果是運(yùn)行時匹配,則創(chuàng)建一個新的動態(tài)方法匹配器實(shí)例并將其加入到攔截器鏈中
                         * 這個對象包含 MethodInterceptor 和 MethodMatcher 的實(shí)例
                         */
                        for (MethodInterceptor interceptor : interceptors) {
                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                        }
                    }
                    else {
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }
            }
        }
        // ......
    // 返回構(gòu)建好的攔截器鏈
    return interceptorList;
}

getInterceptorsAndDynamicInterceptionAdvice() 方法負(fù)責(zé)構(gòu)建適用于特定方法調(diào)用的攔截器鏈。該方法的主要任務(wù)是從 IOC 容器中獲取所有的 Advisor 對象,并根據(jù)目標(biāo)對象及其方法進(jìn)行篩選和適配,最終返回一個實(shí)現(xiàn)或者封裝了 MethodInterceptor 接口的對象組成的列表。

再來看下 proceed() 方法的邏輯:

public Object proceed() throws Throwable {
    /**
     * 索引變量,用來跟蹤當(dāng)前正在處理的攔截器位置。每次調(diào)用 proceed() 方法時,它都會增加,指向下一個要執(zhí)行的攔截器。
     * 這里從索引為 -1 的攔截器開始調(diào)用按序遞增,并檢查是否已經(jīng)到達(dá)攔截器鏈的末尾
     * 如果當(dāng)前索引已經(jīng)是最后一個攔截器,則通過反射直接調(diào)用目標(biāo)方法
     */
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 當(dāng)攔截器鏈中的所有攔截器都已經(jīng)被處理完畢后,調(diào)用此方法來執(zhí)行實(shí)際的目標(biāo)方法(即業(yè)務(wù)邏輯方法)。
        // 這標(biāo)志著攔截器鏈的結(jié)束
        return invokeJoinpoint();
    }

    // 獲取下一個要執(zhí)行的攔截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 如果是動態(tài)切點(diǎn)攔截器,需要在運(yùn)行時評估其是否應(yīng)該激活
        // 靜態(tài)匹配部分已經(jīng)在構(gòu)建攔截器鏈時完成,并且匹配成功
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 使用方法匹配器匹配,判斷是否應(yīng)用此攔截器
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {

            // 匹配成功,則調(diào)用該攔截器的 invoke 方法,繼續(xù)執(zhí)行攔截器邏輯
            return dm.interceptor.invoke(this);
        }
        else {
            // 匹配失敗,跳過當(dāng)前攔截器,遞歸調(diào)用 proceed() 繼續(xù)下一個攔截器
            return proceed();
        }
    }
    else {
        // 直接調(diào)用 MethodInterceptor 的 invoke 方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

proceed() 方法的作用就是按順序逐個執(zhí)行攔截器鏈中的攔截器。它從索引 -1 開始,逐個執(zhí)行攔截器,直至執(zhí)行到攔截器鏈的末尾后,完成對目標(biāo)方法的調(diào)用。

好的,分析了這么多,我們的代理對象到底是如何將事務(wù)管理起來的呢?

事務(wù)的實(shí)現(xiàn)也是通過攔截器來實(shí)現(xiàn)的,這個攔截器是 TransactionInterceptor,它實(shí)現(xiàn)了 MethodInterceptor接口。那么,當(dāng)目標(biāo)方法為事務(wù)方法時,proceed() 方法中調(diào)用的攔截器的 invoke() 方法會調(diào)用到 TransactionInterceptor 類中:

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

    // 調(diào)用內(nèi)部方法 invokeWithinTransaction 來處理事務(wù)邏輯
    // 回調(diào)接口用于提供具體的執(zhí)行邏輯,包括如何繼續(xù)執(zhí)行攔截器鏈或目標(biāo)方法,以及訪問目標(biāo)對象和方法參數(shù)等
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
        @Override
        @Nullable
        public Object proceedWithInvocation() throws Throwable {
            // 繼續(xù)執(zhí)行攔截器鏈中的下一個元素
            return invocation.proceed();
        }
        @Override
        public Object getTarget() {
            // 返回當(dāng)前調(diào)用的目標(biāo)對象實(shí)例
            return invocation.getThis();
        }
        @Override
        public Object[] getArguments() {
            // 返回傳遞給目標(biāo)方法的參數(shù)數(shù)組
            return invocation.getArguments();
        }
    });
}

可以看出,該方法處理帶有事務(wù)管理的目標(biāo)方法調(diào)用主要是通過內(nèi)部方法 invokeWithinTransaction() 實(shí)現(xiàn)的,這個方法會調(diào)用至其父類 TransactionAspectSupport 類中:

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

    // 如果沒有事務(wù)屬性,則該方法是非事務(wù)性的
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 計(jì)算事務(wù)屬性
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    // 根據(jù)屬性配置獲取事務(wù)管理器,沒有配置獲取默認(rèn)事務(wù)管理器
    final TransactionManager tm = determineTransactionManager(txAttr);

    // ......

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

    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { // 聲明式事務(wù)處理
        // 開啟事務(wù),將事務(wù)信息封裝成 TransactionInfo 對象
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

        Object retVal;
        try {
            // 環(huán)繞通知,執(zhí)行目標(biāo)方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            /**
             * 處理目標(biāo)方法調(diào)用期間拋出的異常
             * 根據(jù)在 rollbackFor 中指定的回滾的異常類型匹配,決定事務(wù)回滾或者提交
             */
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // 清理事務(wù)信息
            cleanupTransactionInfo(txInfo);
        }

        // ......
        // 在正常返回后提交事務(wù)
        commitTransactionAfterReturning(txInfo);
        return retVal;
    } else { // 編程式事務(wù)處理
        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) {
                    // 根據(jù)事務(wù)屬性判斷是否需要回滾
                    if (txAttr.rollbackOn(ex)) {
                        // 如果是運(yùn)行時異常,直接拋出
                        if (ex instanceof RuntimeException) {
                            throw (RuntimeException) ex;
                        }
                        else {
                            throw new ThrowableHolderException(ex);
                        }
                    }
                    else {
                        // 非回滾異常,記錄并返回 null
                        throwableHolder.throwable = ex;
                        return null;
                    }
                }
                finally {
                    // 清理事務(wù)信息
                    cleanupTransactionInfo(txInfo);
                }
            });
        }
        catch (ThrowableHolderException ex) {
            throw ex.getCause();
        }

        return result;
    }
}

篇幅原因,我們就不再繼續(xù)深究事務(wù)的控制細(xì)節(jié)了。其實(shí)分析到這里的話,我們也基本了解了當(dāng)一個帶有 @Transactional 注解的方法被調(diào)用時,聲明式事務(wù)究竟是如何被控制的。

那就是,Spring AOP 機(jī)制會使用代理對象來攔截事務(wù)方法的執(zhí)行。最終通過 invokeWithinTransaction() 方法確保目標(biāo)方法在一個適當(dāng)?shù)氖聞?wù)上下文中運(yùn)行。它根據(jù) @Transactional 中配置的事務(wù)屬性選擇合適的事務(wù)管理器,并決定是否需要創(chuàng)建或加入現(xiàn)有事務(wù)。然后,在事務(wù)邊界控制邏輯中,代理對象會在執(zhí)行目標(biāo)方法前開啟事務(wù),在方法正常結(jié)束時提交事務(wù);如果過程中拋出了異常,則根據(jù)事務(wù)屬性配置的規(guī)則進(jìn)行回滾。整個過程自動完成了事務(wù)的開啟、提交和回滾。

總結(jié)

為了方便理解上述整個過程,請看下圖:

圖片圖片

責(zé)任編輯:武曉燕 來源: Java驛站
相關(guān)推薦

2009-06-22 09:01:57

Spring聲明式事務(wù)

2023-05-05 07:39:04

Spring事務(wù)面試

2024-11-13 19:03:14

2022-06-17 08:37:14

分布式事務(wù)分庫分表

2024-06-28 09:07:19

2021-09-06 13:42:14

Spring聲明式事務(wù)

2023-05-12 08:02:43

分布式事務(wù)應(yīng)用

2024-01-26 13:17:00

rollbackMQ訂單系統(tǒng)

2009-02-11 11:14:31

事務(wù)管理事務(wù)開始Spring

2009-02-11 13:08:29

事務(wù)提交事務(wù)管理Spring

2009-06-22 11:01:12

2019-11-19 08:32:26

數(shù)據(jù)庫HLC事務(wù)

2023-11-02 07:52:30

Java工具

2022-06-21 08:27:22

Seata分布式事務(wù)

2025-04-11 09:57:16

2023-09-27 16:22:51

SpringMySQL原子性

2021-06-26 14:59:13

SpringTransaction執(zhí)行

2023-09-28 09:07:54

注解失效場景

2017-04-20 12:30:57

聲明式爬蟲網(wǎng)絡(luò)

2021-04-15 08:01:27

Spring聲明式事務(wù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號