成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring AOP 中的代理對象是怎么創(chuàng)建出來的?

開發(fā) 前端
首先實例化就是通過反射,先把 Bean 的實例創(chuàng)建出來;接下來屬性賦值就是給創(chuàng)建出來的 Bean 的各個屬性賦值;接下來的初始化就是給 Bean 應用上各種需要的后置處理器;最后則是銷毀。

1. AOP 用法

先來一個簡單的案例,小伙伴們先回顧一下 AOP,假設我有如下類:

@Service
public class UserService {

    public void hello() {
        System.out.println("hello javaboy");
    }
}

然后我寫一個切面,攔截 UserService 中的方法:

@Component
@Aspect
@EnableAspectJAutoProxy
public class LogAspect {

    @Before("execution(* org.javaboy.bean.aop.UserService.*(..))")
    public void before(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println(name+" 方法開始執(zhí)行了...");
    }
}

最后,我們看一下從 Spring 容器中獲取到的 UserService 對象:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml");
UserService us = ctx.getBean(UserService.class);
System.out.println("us.getClass() = " + us.getClass());

打印結(jié)果如下:

圖片圖片

可以看到,獲取到的 UserService 是一個代理對象。

其他各種類型的通知我這里就不說了,不熟悉的小伙伴可以在公眾號【江南一點雨】后臺回復 ssm,有松哥錄制的免費入門視頻。

2. 原理分析

那么注入到 Spring 容器中的 UserService,為什么在獲取的時候變成了一個代理對象,而不是原本的 UserService 了呢?

整體上來說,我們可以將 Spring Bean 的生命周期分為四個階段,分別是:

實例化。

屬性賦值。

初始化。

銷毀。

如下圖:

圖片圖片

首先實例化就是通過反射,先把 Bean 的實例創(chuàng)建出來;接下來屬性賦值就是給創(chuàng)建出來的 Bean 的各個屬性賦值;接下來的初始化就是給 Bean 應用上各種需要的后置處理器;最后則是銷毀。

2.1 doCreateBean

AOP 代理對象的創(chuàng)建是在初始化這個過程中完成的,所以今天我們就從初始化這里開始看起。

AbstractAutowireCapableBeanFactory#doCreateBean:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  throws BeanCreationException {
    //...
 try {
  populateBean(beanName, mbd, instanceWrapper);
  exposedObject = initializeBean(beanName, exposedObject, mbd);
 }
 //...
 return exposedObject;
}

小伙伴們看到,這里有一個 initializeBean 方法,在這個方法中會對 Bean 執(zhí)行各種后置處理器:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
 invokeAwareMethods(beanName, bean);
 Object wrappedBean = bean;
 if (mbd == null || !mbd.isSynthetic()) {
  wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
 }
 try {
  invokeInitMethods(beanName, wrappedBean, mbd);
 }
 catch (Throwable ex) {
  throw new BeanCreationException(
    (mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
 }
 if (mbd == null || !mbd.isSynthetic()) {
  wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
 }
 return wrappedBean;
}

這里一共是執(zhí)行了四個方法,也都是非常常見的 Bean 初始化方法:

  1. invokeAwareMethods:執(zhí)行 Aware 接口下的 Bean。
  2. applyBeanPostProcessorsBeforeInitialization:執(zhí)行 BeanPostProcessor 中的前置方法。
  3. invokeInitMethods:執(zhí)行 Bean 的初始化方法 init。
  4. applyBeanPostProcessorsAfterInitialization:執(zhí)行 BeanPostProcessor 中的后置方法。

1、3 這兩個方法很明顯跟 AOP 關系不大,我們自己平時創(chuàng)建的 AOP 對象基本上都是在 applyBeanPostProcessorsAfterInitialization 中進行處理的,我們來看下這個方法:

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
  throws BeansException {
 Object result = existingBean;
 for (BeanPostProcessor processor : getBeanPostProcessors()) {
  Object current = processor.postProcessAfterInitialization(result, beanName);
  if (current == null) {
   return result;
  }
  result = current;
 }
 return result;
}

小伙伴們看到,這里就是遍歷各種 BeanPostProcessor,并執(zhí)行其 postProcessAfterInitialization 方法,將執(zhí)行結(jié)果賦值給 result 并返回。

2.2 postProcessAfterInitialization

BeanPostProcessor 有一個實現(xiàn)類 AbstractAutoProxyCreator,在 AbstractAutoProxyCreator 的 postProcessAfterInitialization 方法中,進行了 AOP 的處理:

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
 if (bean != null) {
  Object cacheKey = getCacheKey(bean.getClass(), beanName);
  if (this.earlyProxyReferences.remove(cacheKey) != bean) {
   return wrapIfNecessary(bean, beanName, cacheKey);
  }
 }
 return bean;
}
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;
 }
 // Create proxy if we have advice.
 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;
 }
 this.advisedBeans.put(cacheKey, Boolean.FALSE);
 return bean;
}

可以看到,首先會嘗試去緩存中獲取代理對象,如果緩存中沒有的話,則會調(diào)用 wrapIfNecessary 方法進行 AOP 的創(chuàng)建。

正常來說,普通 AOP 的創(chuàng)建,前面三個 if 的條件都是不滿足的。第一個 if 是說 beanName 是否是一個 targetSource,顯然我們這里不是;第二個 if 是說這個 Bean 是不是不需代理(結(jié)合上篇文章一起理解),我們這里顯然是需要代理的;第三個 if 的作用我們也在上篇文章中和小伙伴們介紹過,這里就不再贅述了。

關于第二個 if 我多說一句,如果這里進來的是一個切面的 Bean,例如第一小節(jié)中的 LogAspect,這種 Bean 顯然是不需要代理的,所以會在第二個方法中直接返回,如果是其他普通的 Bean,則第二個 if 并不會進來。

所在在 wrapIfNecessary 中,最重要的方法實際上就是兩個:getAdvicesAndAdvisorsForBean 和 createProxy,前者用來找出來所有跟當前類匹配的切面,后者則用來創(chuàng)建代理對象。

2.3 getAdvicesAndAdvisorsForBean

這個方法,說白了,就是查找各種 Advice(通知/增強) 和 Advisor(切面)。來看下到底怎么找的:

AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean:

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
  Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
 List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
 if (advisors.isEmpty()) {
  return DO_NOT_PROXY;
 }
 return advisors.toArray();
}

從這里可看到,這個方法主要就是調(diào)用 findEligibleAdvisors 去獲取到所有的切面,繼續(xù):

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
 List<Advisor> candidateAdvisors = findCandidateAdvisors();
 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
 extendAdvisors(eligibleAdvisors);
 if (!eligibleAdvisors.isEmpty()) {
  eligibleAdvisors = sortAdvisors(eligibleAdvisors);
 }
 return eligibleAdvisors;
}

這里一共有三個主要方法:

  • findCandidateAdvisors:這個方法是查詢到所有候選的 Advisor,說白了,就是把項目啟動時注冊到 Spring 容器中所有切面都找到,由于一個 Aspect 中可能存在多個 Advice,每個 Advice 最終都能封裝為一個 Advisor,所以在具體查找過程中,找到 Aspect Bean 之后,還需要遍歷 Bean 中的方法。
  • findAdvisorsThatCanApply:這個方法主要是從上個方法找到的所有切面中,根據(jù)切點過濾出來能夠應用到當前 Bean 的切面。
  • extendAdvisors:這個是添加一個 DefaultPointcutAdvisor 切面進來,這個切面使用的 Advice 是 ExposeInvocationInterceptor,ExposeInvocationInterceptor 的作用是用于暴露  MethodInvocation 對象到 ThreadLocal 中,如果其他地方需要使用當前的 MethodInvocation 對象,直接通過調(diào)用 currentInvocation 方法取出即可。

接下來我們就來看一下這三個方法的具體實現(xiàn)。

2.3.1 findCandidateAdvisors

AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

@Override
protected List<Advisor> findCandidateAdvisors() {
 List<Advisor> advisors = super.findCandidateAdvisors();
 if (this.aspectJAdvisorsBuilder != null) {
  advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
 }
 return advisors;
}

這個方法的關鍵在于通過 buildAspectJAdvisors 構(gòu)建出所有的切面,這個方法有點復雜:

public List<Advisor> buildAspectJAdvisors() {
 List<String> aspectNames = this.aspectBeanNames;
 if (aspectNames == null) {
  synchronized (this) {
   aspectNames = this.aspectBeanNames;
   if (aspectNames == null) {
    List<Advisor> advisors = new ArrayList<>();
    aspectNames = new ArrayList<>();
    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
      this.beanFactory, Object.class, true, false);
    for (String beanName : beanNames) {
     if (!isEligibleBean(beanName)) {
      continue;
     }
     // We must be careful not to instantiate beans eagerly as in this case they
     // would be cached by the Spring container but would not have been weaved.
     Class<?> beanType = this.beanFactory.getType(beanName, false);
     if (beanType == null) {
      continue;
     }
     if (this.advisorFactory.isAspect(beanType)) {
      aspectNames.add(beanName);
      AspectMetadata amd = new AspectMetadata(beanType, beanName);
      if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
       MetadataAwareAspectInstanceFactory factory =
         new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
       List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
       if (this.beanFactory.isSingleton(beanName)) {
        this.advisorsCache.put(beanName, classAdvisors);
       }
       else {
        this.aspectFactoryCache.put(beanName, factory);
       }
       advisors.addAll(classAdvisors);
      }
      else {
       // Per target or per this.
       if (this.beanFactory.isSingleton(beanName)) {
        throw new IllegalArgumentException("Bean with name '" + beanName +
          "' is a singleton, but aspect instantiation model is not singleton");
       }
       MetadataAwareAspectInstanceFactory factory =
         new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
       this.aspectFactoryCache.put(beanName, factory);
       advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
     }
    }
    this.aspectBeanNames = aspectNames;
    return advisors;
   }
  }
 }
 if (aspectNames.isEmpty()) {
  return Collections.emptyList();
 }
 List<Advisor> advisors = new ArrayList<>();
 for (String aspectName : aspectNames) {
  List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
  if (cachedAdvisors != null) {
   advisors.addAll(cachedAdvisors);
  }
  else {
   MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
   advisors.addAll(this.advisorFactory.getAdvisors(factory));
  }
 }
 return advisors;
}

這個方法第一次進來的時候,aspectNames 變量是沒有值的,所以會先進入到 if 分支中,給 aspectNames 和 aspectBeanNames 兩個變量賦值。

具體過程就是首先調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法(不熟悉該方法的小伙伴參考 Spring 中的父子容器是咋回事?一文),去當前容器以及當前容器的父容器中,查找到所有的 beanName,將返回的數(shù)組賦值給 beanNames 變量,然后對 beanNames 進行遍歷。

遍歷時,首先調(diào)用 isEligibleBean 方法,這個方法是檢查給定名稱的 Bean 是否符合自動代理的條件的,這個細節(jié)我們就不看了,因為一般情況下,我們項目中的 AOP 都是自動代理的。

接下來根據(jù) beanName,找到對應的 bean 類型 beanType,然后調(diào)用 advisorFactory.isAspect 方法去判斷這個 beanType 是否是一個 Aspect,具體的判斷過程上篇文章講過了,小伙伴們可以參考。

如果當前 beanName 對應的 Bean 是一個 Aspect,那么就把 beanName 添加到 aspectNames 集合中,并且把 beanName 和 beanType 封裝為一個 AspectMetadata 對象。

接下來會去判斷 kind 是否為 SINGLETON,這個默認都是 SINGLETON,所以這里會進入到分支中,進來之后,會調(diào)用 this.advisorFactory.getAdvisors 方法去 Aspect 中找到各種通知和切點并封裝成 Advisor 對象返回,由于一個切面中可能定義多個通知,所以最終返回的 Advisor 是一個集合,最后把找到的 Advisor 集合存入到 advisorsCache 緩存中。

后面方法的邏輯就很好懂了,從 advisorsCache 中找到某一個 aspect 對應的所有 Advisor,并將之存入到 advisors 集合中,然后返回集合。

這樣,我們就找到了所有的 Advisor。

2.3.2 findAdvisorsThatCanApply

接下來 findAdvisorsThatCanApply 方法主要是從眾多的 Advisor 中,找到能匹配上當前 Bean 的 Advisor,小伙伴們知道,每一個 Advisor 都包含一個切點 Pointcut,不同的切點意味著不同的攔截規(guī)則,所以現(xiàn)在需要進行匹配,檢查當前類需要和哪個 Advisor 匹配:

protected List<Advisor> findAdvisorsThatCanApply(
  List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
 ProxyCreationContext.setCurrentProxiedBeanName(beanName);
 try {
  return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
 }
 finally {
  ProxyCreationContext.setCurrentProxiedBeanName(null);
 }
}

這里實際上就是調(diào)用了靜態(tài)方法 AopUtils.findAdvisorsThatCanApply 去查找匹配的 Advisor:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
 if (candidateAdvisors.isEmpty()) {
  return candidateAdvisors;
 }
 List<Advisor> eligibleAdvisors = new ArrayList<>();
 for (Advisor candidate : candidateAdvisors) {
  if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
   eligibleAdvisors.add(candidate);
  }
 }
 boolean hasIntroductions = !eligibleAdvisors.isEmpty();
 for (Advisor candidate : candidateAdvisors) {
  if (candidate instanceof IntroductionAdvisor) {
   // already processed
   continue;
  }
  if (canApply(candidate, clazz, hasIntroductions)) {
   eligibleAdvisors.add(candidate);
  }
 }
 return eligibleAdvisors;
}

這個方法中首先會去判斷 Advisor 的類型是否是 IntroductionAdvisor 類型,IntroductionAdvisor 類型的 Advisor 只能在類級別進行攔截,靈活度不如 PointcutAdvisor,所以我們一般都不是 IntroductionAdvisor,因此這里最終會走入到最后一個分支中:

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
 if (advisor instanceof IntroductionAdvisor ia) {
  return ia.getClassFilter().matches(targetClass);
 }
 else if (advisor instanceof PointcutAdvisor pca) {
  return canApply(pca.getPointcut(), targetClass, hasIntroductions);
 }
 else {
  // It doesn't have a pointcut so we assume it applies.
  return true;
 }
}

從這里小伙伴們就能看到,IntroductionAdvisor 類型的 Advisor 只需要調(diào)用 ClassFilter 過濾一下就行了,ClassFilter 松哥在前面的文章中已經(jīng)介紹過了(玩一玩編程式 AOP),小伙伴們看這里的匹配邏輯也是非常 easy!而 PointcutAdvisor 類型的 Advisor 則會繼續(xù)調(diào)用 canApply 方法進行判斷:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
 if (!pc.getClassFilter().matches(targetClass)) {
  return false;
 }
 MethodMatcher methodMatcher = pc.getMethodMatcher();
 if (methodMatcher == MethodMatcher.TRUE) {
  // No need to iterate the methods if we're matching any method anyway...
  return true;
 }
 IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
 if (methodMatcher instanceof IntroductionAwareMethodMatcher iamm) {
  introductionAwareMethodMatcher = iamm;
 }
 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;
}

小伙伴們看一下,這里就是先按照類去匹配,匹配通過則繼續(xù)按照方法去匹配,方法匹配器要是設置的 true,那就直接返回 true 就行了,否則就加載當前類,也就是 targetClass,然后遍歷 targetClass 中的所有方法,最后調(diào)用 introductionAwareMethodMatcher.matches 方法去判斷方法是否和切點契合。

就這樣,我們就從所有的 Advisor 中找到了所有和當前類匹配的 Advisor 了。

2.3.3 extendAdvisors

這個是添加一個 DefaultPointcutAdvisor 切面進來,這個切面使用的 Advice 是 ExposeInvocationInterceptor,ExposeInvocationInterceptor 的作用是用于暴露 MethodInvocation 對象到 ThreadLocal 中,如果其他地方需要使用當前的 MethodInvocation 對象,直接通過調(diào)用 currentInvocation 方法取出即可。

這個方法的邏輯比較簡單,我就不貼出來了,小伙伴們可以自行查看。

2.4 createProxy

看完了 getAdvicesAndAdvisorsForBean 方法,我們已經(jīng)找到了適合我們的 Advisor,接下來繼續(xù)看 createProxy 方法,這個方法用來創(chuàng)建一個代理對象:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
  @Nullable Object[] specificInterceptors, TargetSource targetSource) {
 return buildProxy(beanClass, beanName, specificInterceptors, targetSource, false);
}
private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
  @Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
 if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) {
  AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);
 }
 ProxyFactory proxyFactory = new ProxyFactory();
 proxyFactory.copyFrom(this);
 if (proxyFactory.isProxyTargetClass()) {
  // Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
  if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
   // Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
   for (Class<?> ifc : beanClass.getInterfaces()) {
    proxyFactory.addInterface(ifc);
   }
  }
 }
 else {
  // No proxyTargetClass flag enforced, let's apply our default checks...
  if (shouldProxyTargetClass(beanClass, beanName)) {
   proxyFactory.setProxyTargetClass(true);
  }
  else {
   evaluateProxyInterfaces(beanClass, proxyFactory);
  }
 }
 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 smartClassLoader && classLoader != beanClass.getClassLoader()) {
  classLoader = smartClassLoader.getOriginalClassLoader();
 }
 return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
}

這段代碼不知道小伙伴們看了是否覺得眼熟,這就是前面發(fā)的另類 AOP,編程式 AOP!一文中的內(nèi)容,所以這塊源碼大家自己看看就好了,我就不啰嗦了。


責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2022-09-29 09:17:47

進程Linux創(chuàng)建

2023-02-27 08:09:42

SpringAOP代理

2023-10-08 10:14:12

2022-09-01 10:40:29

SpringAOPJDK

2024-04-01 08:38:57

Spring@AspectAOP

2019-11-29 16:21:22

Spring框架集成

2012-07-11 14:31:16

SpringAop

2022-06-24 09:36:47

Python對象調(diào)用

2021-05-14 00:00:15

JavaScript開發(fā)代碼

2025-01-16 08:45:48

2023-03-29 08:24:30

2021-07-01 10:45:18

Bean對象作用域

2020-06-22 08:50:27

Spring AOP代理

2021-07-27 22:56:00

JavaScript編程開發(fā)

2024-11-04 16:29:19

2024-03-04 07:41:18

SpringAOPOOP?

2021-10-27 07:15:37

SpringAOP編程(

2024-11-28 11:59:57

2010-04-26 08:53:06

面向方面編程.NET

2009-06-16 15:02:18

面向?qū)ο缶幊?/a>PHP異常PHP代理
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩av视屏 | 欧美二区在线 | 国产精品久久久久久久久 | 国产精品国产成人国产三级 | 日本男人天堂 | 久久久久亚洲精品 | 久久久av一区 | 一区二区高清 | 久久久久久色 | 自拍视频网站 | 特黄毛片 | 中文字幕一区在线观看视频 | 日本在线视 | 亚洲免费视频在线观看 | 久国久产久精永久网页 | 国产成人精品一区二区三区网站观看 | 午夜影院官网 | 小h片免费观看久久久久 | 日本一区二区三区在线观看 | 免费三级黄 | 在线免费观看黄色 | 一级毛片,一级毛片 | 精品一区二区在线观看 | 欧美视频免费 | 91视频a| 99久久精品免费看国产免费软件 | 男插女下体视频 | 99热热| 337p日本欧洲亚洲大胆鲁鲁 | 很黄很污的网站 | 亚洲成人一区二区 | 日本中文字幕在线视频 | 午夜精品视频在线观看 | 中文字幕1区 | 成人做爰www免费看 午夜精品久久久久久久久久久久 | 第一av | 在线三级电影 | 亚洲视频 欧美视频 | 欧美一级片黄色 | av中文网| 日韩欧美在线观看 |