精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

基于 @Transactional 的聲明式事務原理剖析

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

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

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


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

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

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

代理生成

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

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

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

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

在 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 進行了后置處理,處理 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 注解標記的類肯定是要生成代理才能執(zhí)行事務邏輯的,我們繼續(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;
    }

    // 關鍵方法,這里查找是否有匹配的 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;
    }

    // 標記該類不需要加強,并返回普通的 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;
    }
}

執(zhí)行到此處后,我們找到了處理事務的 Advisor 為 BeanFactoryTransactionAttributeSourceAdvisor,然后再次執(zhí)行重載的 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;
    }

    // 創(chuàng)建一個 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 方法,所以執(zhí)行方法匹配時,會走到如下邏輯:

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 方法事務會失效的原因;
  • 同時,從查找事務屬性的執(zhí)行邏輯來看,這段代碼也證明了 @Transactional 注解方法級別的配置優(yōu)先于類級別的配置。

上述邏輯中的 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 規(guī)范下的 javax.transaction.Transactional 注解。
  • SpringTransactionAnnotationParser:用于解析 Spring 框架的 @Transactional 注解。

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

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

parseTransactionAnnotation() 方法中又調用了其重載方法,執(zhí)行具體的屬性解析邏輯,下面我們看下這段解析代碼的邏輯,相信大家看到這里應該會非常熟悉,這里解析的就是我們在 @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 的,接下來就可以創(chuàng)建代理了。

我們可以回到上面 AbstractAutoProxyCreator 類的 wrapIfNecessary() 方法中看下,如果 getAdvicesAndAdvisorsForBean() 返回不為空,那么就要為當前 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);

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

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

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

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

事務管理

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

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

上述創(chuàng)建代理邏輯中通過 proxyFactory.getProxy(classLoader); 去獲取代理對象時,實際生產代理對象的代理類有兩個默認實現 CglibAopProxy 與 JdkDynamicAopProxy,具體執(zhí)行哪個類去生產代理對象也是通過 proxyTargetClass 屬性與目標類是否實現接口來判斷的。下邊我們以 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()) {
            // 攔截器鏈為空,直接通過反射調用目標方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 創(chuàng)建一個包含代理對象、目標對象、方法、參數等信息的 MethodInvocation 實例
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 通過攔截器鏈執(zhí)行方法調用
            retVal = invocation.proceed();
        }

        return retVal;
      }
}

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

ReflectiveMethodInvocation 是 Spring AOP 內部用于表示一次具體方法調用的對象,它封裝了實際的目標方法調用,并且負責管理攔截器鏈的執(zhí)行流程。

我們首先來看下獲取攔截器鏈的邏輯,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()) {
                        /**
                         * 如果是運行時匹配,則創(chuàng)建一個新的動態(tài)方法匹配器實例并將其加入到攔截器鏈中
                         * 這個對象包含 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() 方法時,它都會增加,指向下一個要執(zhí)行的攔截器。
     * 這里從索引為 -1 的攔截器開始調用按序遞增,并檢查是否已經到達攔截器鏈的末尾
     * 如果當前索引已經是最后一個攔截器,則通過反射直接調用目標方法
     */
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 當攔截器鏈中的所有攔截器都已經被處理完畢后,調用此方法來執(zhí)行實際的目標方法(即業(yè)務邏輯方法)。
        // 這標志著攔截器鏈的結束
        return invokeJoinpoint();
    }

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

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

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

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

事務的實現也是通過攔截器來實現的,這個攔截器是 TransactionInterceptor,它實現了 MethodInterceptor接口。那么,當目標方法為事務方法時,proceed() 方法中調用的攔截器的 invoke() 方法會調用到 TransactionInterceptor 類中:

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

    // 調用內部方法 invokeWithinTransaction 來處理事務邏輯
    // 回調接口用于提供具體的執(zhí)行邏輯,包括如何繼續(xù)執(zhí)行攔截器鏈或目標方法,以及訪問目標對象和方法參數等
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
        @Override
        @Nullable
        public Object proceedWithInvocation() throws Throwable {
            // 繼續(xù)執(zhí)行攔截器鏈中的下一個元素
            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 {
            // 環(huán)繞通知,執(zhí)行目標方法
            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;
    }
}

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

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

總結

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

圖片圖片

責任編輯:武曉燕 來源: 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訂單系統(tǒng)

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分布式事務

2025-04-11 09:57:16

2025-09-30 08:27:18

Spring數據庫編程式

2025-10-09 01:11:00

Spring數據庫事務

2023-09-27 16:22:51

SpringMySQL原子性

2023-09-28 09:07:54

注解失效場景

2021-06-26 14:59:13

SpringTransaction執(zhí)行
點贊
收藏

51CTO技術棧公眾號

色综合久久综合网97色综合| 亚洲国产精品一区制服丝袜| 色婷婷亚洲婷婷| 精品国产一区二区三区麻豆免费观看完整版 | 国产精品福利一区二区三区| 国产美女直播视频一区| 国产精品无码无卡无需播放器| av成人免费| 亚洲激情第一区| 好看的日韩精品视频在线| 欧产日产国产69| 成人午夜国产| 欧美成人猛片aaaaaaa| 国产真实老熟女无套内射| 欧美一区二不卡视频| 香蕉国产精品偷在线观看不卡| 亚洲欧美另类在线观看| 色噜噜狠狠一区二区| a黄色片在线观看| 不卡的av中国片| 国产高清在线不卡| 69夜色精品国产69乱| 日本一区二区乱| 精品成人乱色一区二区| 亚洲精美视频| 亚洲免费黄色片| 久久久久久9| 日韩日本欧美亚洲| av在线播放网址| 九七影院97影院理论片久久| 一区二区三区高清不卡| 欧美日韩电影一区二区| 国产情侣在线播放| 午夜影院日韩| 欧美尺度大的性做爰视频| 妖精视频一区二区| 欧美aaaaaaaa| 欧美日韩精品二区| 午夜欧美性电影| 熟妇高潮一区二区三区| 国内精品伊人久久久久av一坑| 91国内免费在线视频| sm捆绑调教视频| 免费精品国产的网站免费观看| 日韩一区二区三区免费看| 欧美牲交a欧美牲交aⅴ免费真 | 一二美女精品欧洲| 黄色免费视频网站| 欧州一区二区三区| 欧美性videosxxxxx| 欧美一级视频免费看| 久久精品视频免费看| 久久久午夜精品| 99久久一区三区四区免费| av大片免费观看| 亚洲精品女人| 久久人人爽人人| 国产av 一区二区三区| 国产最新精品| 亚洲精品一区av在线播放| 日本亚洲一区二区三区| 久久久精品一区二区毛片免费看| 精品欧美aⅴ在线网站| 91视频 - 88av| 久久精品视频观看| 91麻豆免费观看| 国精产品99永久一区一区| 国产99久久九九精品无码免费| 免费人成在线不卡| 日本sm极度另类视频| 黄色片视频网站| 亚洲福利久久| 97国产精品人人爽人人做| 免费成人美女女在线观看| 成人羞羞网站入口免费| 一区二区在线视频| 日本美女bbw| 成人同人动漫免费观看| 一本一本久久a久久精品牛牛影视 一本色道久久综合亚洲精品小说 一本色道久久综合狠狠躁篇怎么玩 | 欧美大奶子在线| 久热这里有精品| 婷婷综合久久| 欧美精品免费看| 国产亚洲精品成人| 欧美日韩精品免费观看视频完整| 欧美成人网在线| 国产精品二区一区二区aⅴ| 欧美日本不卡| 97视频免费在线观看| 日本特黄特色aaa大片免费| 国产综合精品| 欧美大片在线看| 欧美日韩亚洲国产另类| 欧美午夜一区| 97成人在线视频| 99精品在线播放| 热久久久久久久| 91精品综合视频| 亚洲第一天堂影院| 国产精品影视天天线| av在线亚洲男人的天堂| 天堂中文网在线| 久久久久久久精| 亚洲精品乱码久久久久久蜜桃91| 国产激情在线| 亚洲va韩国va欧美va| 成人黄色片视频| 欧美国产视频| 精品国产青草久久久久福利| 久久久无码人妻精品一区| 精品日本12videosex| 久久精品人人做人人爽| 精品少妇久久久| 激情综合在线| 国产精品久久久久久久久久免费 | 在线激情网站| 一区二区三区四区中文字幕| 你真棒插曲来救救我在线观看| jizz内谢中国亚洲jizz| 制服丝袜亚洲色图| 黄色片视频免费观看| 成人影院在线| 久久久久久亚洲精品中文字幕| 美女又爽又黄免费视频| 国内不卡的二区三区中文字幕| 高清视频一区二区三区| 成人精品福利| 亚洲国产一区二区a毛片| 天堂中文视频在线| 亚洲网一区二区三区| 亚洲丝袜在线视频| 久草视频免费播放| 免费成人在线网站| 国产在线一区二| 黄色在线免费| 日本久久电影网| 国产伦精品一区二区三区88av| 欧美熟乱15p| 欧美一二三视频| 成人激情四射网| 久久久久久亚洲综合| 欧美另类videosbestsex日本| 欧美天堂视频| 亚洲精品小视频在线观看| 欧美成人手机视频| 老司机精品视频一区二区三区| 国产精品久久久久久久久久直播| 久cao在线| 欧美亚洲国产一卡| 美国黄色一级毛片| 欧美黄色大片网站| 国产在线播放91| 国产资源在线观看| 一本色道综合亚洲| 国产伦精品一区二区三区精品| 亚洲精品电影| 国产在线视频2019最新视频| 超碰97在线免费观看| 日韩欧美国产激情| 麻豆精品国产传媒av| 欧美久久久久| av在线不卡观看| 欧美人与牲禽动交com| 日韩一区二区免费电影| 男人在线观看视频| 捆绑调教一区二区三区| 五月天综合网| 成人精品国产亚洲| 一本色道久久综合狠狠躁篇的优点| 日韩网红少妇无码视频香港| 国产一区二区在线观看视频| 国产av第一区| 日本一区二区三区视频在线看| 日韩一区在线视频| 国产精品久久久久精| 亚洲人123区| 57pao国产成永久免费视频| 日韩精品电影| 国产精品一区二区久久久久| 1024免费在线视频| 欧美日韩精品高清| 亚洲国产精品一区二区久久hs| 麻豆成人免费电影| 久久久国产精华液999999| vam成人资源在线观看| 久久精品99无色码中文字幕 | 亚洲精品资源| 久久久久久九九| 春暖花开亚洲一区二区三区| 亚洲一级免费视频| 夜夜骚av一区二区三区| 亚洲桃色在线一区| 日本天堂在线播放| 米奇777在线欧美播放| 色之综合天天综合色天天棕色| www.一区| 九九九热精品免费视频观看网站| www夜片内射视频日韩精品成人| 亚洲综合色区另类av| 少妇一级淫免费观看| 日韩成人一级片| 天天综合中文字幕| aaa国产精品视频| 日产日韩在线亚洲欧美| 欧美jizz18hd性欧美| 精品国产乱码久久久久久免费 | 九九视频免费观看视频精品| 国产v综合v亚洲欧美久久| 美女黄视频在线观看| 亚洲成色999久久网站| 国产情侣小视频| 亚洲三级在线免费观看| 青青草视频成人| 精品一区二区三区影院在线午夜 | 中文字幕中文在线不卡住| 337p日本欧洲亚洲大胆张筱雨| 校园激情久久| 中文字幕第一页亚洲| 日韩精品导航| 亚洲一区二区三区四区视频| 在线观看爽视频| 久久精品亚洲国产| 欧美成人免费| 欧美mv和日韩mv国产网站| 欧美一级黄视频| 一区二区在线观看视频 | 国产欧美日韩不卡| 亚洲妇女无套内射精| 石原莉奈在线亚洲二区| 50度灰在线观看| 国产日产精品_国产精品毛片| 国产69精品久久久久9999apgf | 国产又大又黄又粗| 亚洲精品国产一区二区精华液 | 亚洲日本va中文字幕| 国产精品久久网| 少妇在线看www| 欧美另类第一页| 欧美三级黄网| 国产一区二区三区欧美| 欧美一级特黄aaaaaa大片在线观看| 欧美剧情电影在线观看完整版免费励志电影 | 久久久无码精品亚洲日韩按摩| 亚洲一区二区三区三州| 青青草精品视频| 免费av网址在线| 亚洲日韩视频| 日本一区午夜艳熟免费| 婷婷精品进入| 色之综合天天综合色天天棕色| 欧美freesex8一10精品| 99精品国产一区二区| 宅男噜噜噜66国产精品免费| 国产精品高潮呻吟久久av黑人| 久久久男人天堂| 久久久久国产精品www| 国产在线高清视频| 俺去了亚洲欧美日韩| 成年人在线观看| 国产亚洲成av人片在线观看桃| 欧美香蕉爽爽人人爽| 日韩精品在线观看一区| 性插视频在线观看| 亚洲精品一区二区三区在线观看 | 久久综合久久久| 色先锋久久影院av| 久久国产一区二区| 免费成人三级| 久久免费视频1| 欧美**vk| 日韩精品国内| 亚洲一区二区| 9久久9毛片又大又硬又粗| 久久国产精品99国产| 69久久久久久| 国产99精品国产| 扒开jk护士狂揉免费| 国产精品卡一卡二卡三| 午夜写真片福利电影网| 婷婷成人综合网| 91美女精品网站| 亚洲国产天堂久久综合网| 久蕉在线视频| 理论片在线不卡免费观看| 国产直播在线| 国产一区私人高清影院| 欧美电影在线观看免费| 一区二区91美女张开腿让人桶| 韩国久久久久| 国产又猛又黄的视频| 波多野结衣精品在线| 欧美 日韩 国产 成人 在线观看| 亚洲精品成人少妇| 激情网站在线观看| 欧美不卡一区二区| 国产高清在线| 国精产品一区一区三区有限在线| 国产一区二区主播在线| caoporn国产精品免费公开| 国产一区二区三区四区二区| 日本一道在线观看| 视频在线观看91| 国产污在线观看| 中文字幕一区二区不卡| 欧美videossex极品| 日韩欧美卡一卡二| 风间由美一区| 91av在线国产| 成人台湾亚洲精品一区二区| 亚洲一区二区三区加勒比| 免费视频一区| 亚洲少妇18p| 亚洲自拍欧美精品| 在线免费一级片| 亚洲欧美一区二区激情| 男人天堂亚洲天堂| 91亚洲精品在线观看| 国产欧美日韩精品高清二区综合区| 蜜臀av色欲a片无码精品一区| 国产在线视频一区二区三区| 久久亚洲无码视频| 欧美日韩国产一区二区三区| 亚洲精品久久久久avwww潮水| 精品国产美女在线| 日本成人福利| 区一区二区三区中文字幕| 一区二区三区国产盗摄| 97免费公开视频| 亚洲欧美日韩在线| 欧美性受xxx黑人xyx性爽| 亚洲另类激情图| 欧美巨大丰满猛性社交| 国产欧美丝袜| 亚洲精品孕妇| 玖草视频在线观看| 五月激情综合婷婷| 女人18毛片一区二区三区| 欧美成人免费全部| 日本伊人久久| 波多野结衣av一区二区全免费观看| 麻豆国产精品官网| 女同久久另类69精品国产| 欧美日韩国产123区| 自拍视频在线免费观看| 国产精品久久久久久久久久东京 | 手机av免费观看| 亚洲人成亚洲人成在线观看| 日韩电影网站| 日韩欧美视频第二区| 免费成人av资源网| 成人性视频免费看| 欧美日韩视频在线一区二区| 一区二区三区视频在线观看视频| 国产精品高潮呻吟视频| 欧洲三级视频| 亚洲激情在线看| 亚洲乱码国产乱码精品精98午夜| 精品毛片在线观看| 国内精品久久久久| 五月激激激综合网色播| 少妇人妻互换不带套| 国产精品女人毛片| 国产同性人妖ts口直男| 欧美国产高跟鞋裸体秀xxxhd| 国产精品sss在线观看av| 国产中文字幕视频在线观看| 久久精品免费在线观看| 一级片免费观看视频| 欧美国产日韩一区二区| 极品国产人妖chinesets亚洲人妖| 波多野结衣家庭教师在线| 国产欧美日本一区视频| 国产一区二区麻豆| 欧美精品www在线观看| 亚洲国产最新| 精品久久久99| 亚洲国产wwwccc36天堂| 极品白浆推特女神在线观看 | 亚洲精品一级片| 国产91精品不卡视频| 日韩一级毛片| 欧美激情一区二区三区p站| 韩曰欧美视频免费观看| 中文字幕日本在线| 懂色av一区二区三区在线播放| 久久九九电影| 欧美成人精品欧美一| 国产视频亚洲视频| 91麻豆精品一二三区在线| 国产精品入口芒果| 日本一区二区三区在线不卡| www.好吊色| 国产精品高潮呻吟久久av野狼| 国产精品jizz在线观看美国| 一级黄色性视频| 精品久久一二三区| 欧美日韩五区| 国产综合中文字幕| 亚洲欧洲精品成人久久奇米网| 色噜噜一区二区三区|