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

透過源碼,捋清楚循環依賴到底是如何解決的!

開發 前端
小伙伴們知道,獲取 Bean 涉及到的就是 getBean 方法,像我們上面這個案例,由于都是單例的形式,所以 Bean 的初始化其實在容器創建的時候就完成了。

以下內容基于 Spring6.0.4。

關于 Spring 循環依賴,松哥已經連著發了三篇文章了,本篇文章松哥從源碼的角度來和小伙伴們捋一捋 Spring 循環依賴到底是如何解決了。

小伙伴們一定要先熟悉前面文章的內容,否則今天的源碼可能會看起來有些吃力。

接下來我通過一個簡單的循環依賴的案例,來和大家梳理一下完整的 Bean 循環依賴處理流程。

1. 案例設計

假設我有如下 Bean:

@Service
public class A {
    @Autowired
    B b;
}
@Service
public class B {
    @Autowired
    A a;
}

就這樣一個簡單的循環依賴,默認情況下,A 會被先加載,然后在 A 中做屬性填充的時候,去創建了 B,創建 B 的時候又需要 A,就會從緩存中拿到 A,大致流程如此,接下來我們結合源碼來驗證一下這個流程。

2. 源碼分析

首先我們來看獲取 Bean 的時候,如何利用這三級緩存。

小伙伴們知道,獲取 Bean 涉及到的就是 getBean 方法,像我們上面這個案例,由于都是單例的形式,所以 Bean 的初始化其實在容器創建的時候就完成了。

圖片圖片

在 preInstantiateSingletons 方法中,又調用到 AbstractBeanFactory#getBean 方法,進而調用到 AbstractBeanFactory#doGetBean 方法。

圖片圖片

Bean 的初始化就是從這里開始的,我們就從這里來開始看起吧。

2.1 doGetBean

AbstractBeanFactory#doGetBean(方法較長,節選部分關鍵內容):

protected <T> T doGetBean(
  String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
  throws BeansException {
 String beanName = transformedBeanName(name);
 Object beanInstance;
 // Eagerly check singleton cache for manually registered singletons.
 Object sharedInstance = getSingleton(beanName);
 if (sharedInstance != null && args == null) {
  beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
 }
 else {
  // Fail if we're already creating this bean instance:
  // We're assumably within a circular reference.
  if (isPrototypeCurrentlyInCreation(beanName)) {
   throw new BeanCurrentlyInCreationException(beanName);
  }
  // Check if bean definition exists in this factory.
  BeanFactory parentBeanFactory = getParentBeanFactory();
  if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
   // Not found -> check parent.
   String nameToLookup = originalBeanName(name);
   if (parentBeanFactory instanceof AbstractBeanFactory abf) {
    return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
   }
   else if (args != null) {
    // Delegation to parent with explicit args.
    return (T) parentBeanFactory.getBean(nameToLookup, args);
   }
   else if (requiredType != null) {
    // No args -> delegate to standard getBean method.
    return parentBeanFactory.getBean(nameToLookup, requiredType);
   }
   else {
    return (T) parentBeanFactory.getBean(nameToLookup);
   }
  }
  if (!typeCheckOnly) {
   markBeanAsCreated(beanName);
  }
  StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
    .tag("beanName", name);
  try {
   if (requiredType != null) {
    beanCreation.tag("beanType", requiredType::toString);
   }
   RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
   checkMergedBeanDefinition(mbd, beanName, args);
   // Guarantee initialization of beans that the current bean depends on.
   String[] dependsOn = mbd.getDependsOn();
   if (dependsOn != null) {
    for (String dep : dependsOn) {
     if (isDependent(beanName, dep)) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
     }
     registerDependentBean(dep, beanName);
     try {
      getBean(dep);
     }
     catch (NoSuchBeanDefinitionException ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
     }
    }
   }
   // Create bean instance.
   if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
     try {
      return createBean(beanName, mbd, args);
     }
     catch (BeansException ex) {
      // Explicitly remove instance from singleton cache: It might have been put there
      // eagerly by the creation process, to allow for circular reference resolution.
      // Also remove any beans that received a temporary reference to the bean.
      destroySingleton(beanName);
      throw ex;
     }
    });
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
   }
  }
 }
 return adaptBeanInstance(name, beanInstance, requiredType);
}

這個方法比較長,我來和大家說幾個關鍵的點:

  1. 首先這個方法一開始就調用了 getSingleton 方法,這個是嘗試從三級緩存中獲取到想要的 Bean,但是,當我們第一次初始化 A 的時候,很顯然這一步是無法獲取到 A 的實例的,所以這一步會返回 null。
  2. 如果第一步拿到了 Bean,那么接下來就進入到 if 分支中,直接獲取到想要的 beanInstance 實例;否則進入到第三步。
  3. 如果第一步沒有從三級緩存中拿到 Bean,那么接下來就要檢查是否是循環依賴了,首先調用 isPrototypeCurrentlyInCreation 方法判斷當前 Bean 是否已經在創建了,如果已經在創建了,那么顯然要拋異常出去了(BeanCurrentlyInCreationException)。接下來就去 parent 容器中各種查找,看能否找到需要的 Bean,Spring 中的父子容器問題松哥在之前的文章中也已經講過了,小伙伴們可以參考:Spring 中的父子容器是咋回事?。
  4. 如果從父容器中也沒找到 Bean,那么接下來就會調用 markBeanAsCreated 方法來標記當前 Bean 已經創建或者正準備創建。
  5. 接下來會去標記一下創建步驟,同時檢查一下 Bean 的 dependsOn 屬性是否存在循環關系,這些跟我們本文關系都不大,我就不去展開了。
  6. 關鍵點來了,接下來判斷如果我們當前 Bean 是單例的,那么就調用 getSingleton 方法去獲取一個實例,該方法的第二個參數一個 Lambda 表達式,表達式的核心內容就是調用 createBean 方法去創建一個 Bean 實例,該方法將不負眾望,拿到最終想要的 Bean。

以上就是 doGetBean 方法中幾個比較重要的點。

其中有兩個方法我們需要展開講一下,第一個方法就是去三級緩存中查詢 Bean 的 getSingleton 方法(步驟一),第二個方法則是去獲取到 Bean 實例的 getSingleton 方法(步驟六),這是兩個重載方法。

接下來我們就來分析一下這兩個方法。

2.2 查詢三級緩存

DefaultSingletonBeanRegistry#getSingleton:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 // Quick check for existing instance without full singleton lock
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  singletonObject = this.earlySingletonObjects.get(beanName);
  if (singletonObject == null && allowEarlyReference) {
   synchronized (this.singletonObjects) {
    // Consistent creation of early reference within full singleton lock
    singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
     singletonObject = this.earlySingletonObjects.get(beanName);
     if (singletonObject == null) {
      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
      if (singletonFactory != null) {
       singletonObject = singletonFactory.getObject();
       this.earlySingletonObjects.put(beanName, singletonObject);
       this.singletonFactories.remove(beanName);
      }
     }
    }
   }
  }
 }
 return singletonObject;
}
  • 首先去 singletonObjects 中查找,這就是所謂的一級緩存,如果這里能直接找到想要的對象,那么直接返回即可。
  • 如果一級緩存中不存在想要的 Bean,那么接下來就該去二級緩存 earlySingletonObjects 中查找了,二級緩存要是有我們想要的 Bean,那么也是直接返回即可。
  • 二級緩存中如果也不存在,那么就是加鎖然后去三級緩存中查找了,三級緩存是 singletonFactories,我們從 singletonFactories 中獲取到的是一個 ObjectFactory 對象,這是一個 Lambda 表達式,調用這里的 getObject 方法最終有可能會促成提前 AOP,至于這個 Lambda 表達式的內容,松哥在前面的文章中已經和小伙伴們介紹過了,這里先不啰嗦(如何通過三級緩存解決 Spring 循環依賴)。
  • 如果走到三級緩存這一步了,從三級緩存中拿到了想要的數據,那么就把數據存入到二級緩存 earlySingletonObjects 中,以備下次使用。同時,移除三級緩存中對應的數據。

當我們第一次創建 A 對象的時候,很顯然三級緩存中都不可能有數據,所以這個方法最終返回 null。

2.3 獲取 Bean 實例

接下來看 2.1 小節步驟六的獲取 Bean 的方法。

DefaultSingletonBeanRegistry#getSingleton(方法較長,節選部分關鍵內容):

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
 synchronized (this.singletonObjects) {
  Object singletonObject = this.singletonObjects.get(beanName);
  if (singletonObject == null) {
   if (this.singletonsCurrentlyInDestruction) {
    throw new BeanCreationNotAllowedException(beanName,
      "Singleton bean creation not allowed while singletons of this factory are in destruction " +
      "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
   }
   beforeSingletonCreation(beanName);
   boolean newSingleton = false;
   boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
   if (recordSuppressedExceptions) {
    this.suppressedExceptions = new LinkedHashSet<>();
   }
   try {
    singletonObject = singletonFactory.getObject();
    newSingleton = true;
   }
   if (newSingleton) {
    addSingleton(beanName, singletonObject);
   }
  }
  return singletonObject;
 }
}
  1. 這個方法首先也是嘗試從一級緩存中獲取到想要的 Bean,如果 Bean 為 null,就開始施法了。
  2. 首先會去判斷一下,如果這個工廠的單例正在銷毀,那么這個 Bean 的創建就不被允許。
  3. 接下來會有一堆準備工作,關鍵點在 singletonFactory.getObject(); 地方,這個就是方法第二個參數傳進來的回調函數,將來在回調函數中,會調用到 createBean 方法,真正開始 A 這個 Bean 的創建。將 A 對象創建成功之后,會把 newSingleton 設置為 true,第 4 步會用到。
  4. 現在調用 addSingleton 方法,把創建成功的 Bean 添加到緩存中。

我們來看下 addSingleton 方法:

protected void addSingleton(String beanName, Object singletonObject) {
 synchronized (this.singletonObjects) {
  this.singletonObjects.put(beanName, singletonObject);
  this.singletonFactories.remove(beanName);
  this.earlySingletonObjects.remove(beanName);
  this.registeredSingletons.add(beanName);
 }
}

小伙伴們看一下,一級緩存中存入 Bean,二級緩存和三級緩存移除該 Bean,同時在 registeredSingletons 集合中記錄一下當前 Bean 已經創建。

所以現在的重點其實又回到了 createBean 方法了。

2.4 createBean

createBean 方法其實就到了 Bean 的創建流程了。bean 的創建流程在前面幾篇 Spring 源碼相關的文章中也都有所涉獵,所以今天我就光說一些跟本文主題相關的幾個點。

createBean 方法最終會調用到 AbstractAutowireCapableBeanFactory#doCreateBean 方法,這個方法也是比較長的,而我是關心如下幾個地方:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  throws BeanCreationException {
 // Eagerly cache singletons to be able to resolve circular references
 // even when triggered by lifecycle interfaces like BeanFactoryAware.
 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
   isSingletonCurrentlyInCreation(beanName));
 if (earlySingletonExposure) {
  addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
 }
 // Initialize the bean instance.
 Object exposedObject = bean;
 try {
  populateBean(beanName, mbd, instanceWrapper);
  exposedObject = initializeBean(beanName, exposedObject, mbd);
 }
 return exposedObject;
}

這里我比較在意的有兩個地方,一個是調用 addSingletonFactory 方法向三級緩存中添加回調函數,回調函數是 getEarlyBeanReference,如果有需要,將來會通過這個回調提前進行 AOP,即使沒有 AOP,就是普通的循環依賴,三級緩存也是會被調用的,這個大家繼續往后看就知道了,另外還有一個比較重要的地方,在本方法一開始的時候,就已經創建出來 A 對象了,這個時候的 A 對象是一個原始 Bean,即單純的只是通過反射把對象創建出來了,Bean 還沒有經歷過完整的生命周期,這里 getEarlyBeanReference 方法的第三個參數就是該 Bean,這個也非常重要,牢記,后面會用到。

第二個地方就是 populateBean 方法,當執行到這個方法的時候,A 對象已經創建出來了,這個方法是給 A 對象填充屬性用的,因為接下來要注入 B 對象,就在這個方法中完成的。

由于我們第 1 小節是通過 @Autowired 來注入 Bean 的,所以現在在 populateBean 方法也主要是處理 @Autowired 注入的情況,那么這個松哥之前寫過文章,小伙伴們參考@Autowired 到底是怎么把變量注入進來的?,具體的注入細節我這里就不重復了,單說在注入的過程中,會經過一個 DefaultListableBeanFactory#doResolveDependency 方法,這個方法就是用來解析 B 對象的(至于如何到達 doResolveDependency 方法的,小伙伴們參考 @Autowired 到底是怎么把變量注入進來的?一文)。

doResolveDependency 方法也是比較長,我這里貼出來和本文相關的幾個關鍵地方:

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
     //...
  Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
  if (matchingBeans.isEmpty()) {
   if (isRequired(descriptor)) {
    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
   }
   return null;
  }
  String autowiredBeanName;
  Object instanceCandidate;
  if (matchingBeans.size() > 1) {
   autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
   if (autowiredBeanName == null) {
    if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
     return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
    }
    else {
     // In case of an optional Collection/Map, silently ignore a non-unique case:
     // possibly it was meant to be an empty collection of multiple regular beans
     // (before 4.3 in particular when we didn't even look for collection beans).
     return null;
    }
   }
   instanceCandidate = matchingBeans.get(autowiredBeanName);
  }
  else {
   // We have exactly one match.
   Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
   autowiredBeanName = entry.getKey();
   instanceCandidate = entry.getValue();
  }
  if (autowiredBeanNames != null) {
   autowiredBeanNames.add(autowiredBeanName);
  }
  if (instanceCandidate instanceof Class) {
   instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  }
        //...
}
  1. 在這個方法中,首先調用 findAutowireCandidates 方法,以類型為依據,找到所有滿足條件的 Class 并組成一個 Map 返回。例如第一小節的案例,這里就會找到所有 B 類型的 Class,通過一個 Map 返回。
  2. 如果第一步返回的 Map 存在多條記錄,那么就必須從中挑選一個出來,這就是 matchingBeans.size() > 1 的情況。
  3. 如果第一步返回的 Map 只有一條記錄,那么就從 Map 中提取出來 key 和 value,此時的 value 是一個 Class,所以接下來還要調用 descriptor.resolveCandidate 去完成 Class 到對象的轉變。

而 descriptor.resolveCandidate 方法又開啟了新一輪的 Bean 初始化,只不過這次初始化的 B 對象,如下:

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
  throws BeansException {
 return beanFactory.getBean(beanName);
}

2.5 后續流程

后續流程其實就是上面的步驟,我就直接來跟大家說一說,就不貼代碼了。

現在系統調用 beanFactory.getBean 方法去查找 B 對象,結果又是走一遍本文第二小節的所有流程,當 B 創建出來之后,也要去做屬性填充,此時需要在 B 中注入 A,那么又來到本文的 2.4 小節,最終又是調用到 resolveCandidate 方法去獲取 A 對象。

此時,在獲取 A 對象的過程中,又會調用到 doGetBean 這個方法,在這個方法中調用 getSingleton 的時候(2.1 小節的第一步),這個時候的執行邏輯就跟前面不一樣了,我們再來看下這個方法的源碼:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 // Quick check for existing instance without full singleton lock
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  singletonObject = this.earlySingletonObjects.get(beanName);
  if (singletonObject == null && allowEarlyReference) {
   synchronized (this.singletonObjects) {
    // Consistent creation of early reference within full singleton lock
    singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
     singletonObject = this.earlySingletonObjects.get(beanName);
     if (singletonObject == null) {
      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
      if (singletonFactory != null) {
       singletonObject = singletonFactory.getObject();
       this.earlySingletonObjects.put(beanName, singletonObject);
       this.singletonFactories.remove(beanName);
      }
     }
    }
   }
  }
 }
 return singletonObject;
}

現在還是嘗試從三級緩存中獲取 A,此時一二級緩存中還是沒有 A,但是三級緩存中有一個回調函數,當執行 singletonFactory.getObject() 方法的時候,就會觸發該回調函數,這個回調函數就是我們前面 2.4 小節提到的 getEarlyBeanReference 方法,我們現在來看下這個方法:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
 Object exposedObject = bean;
 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
   exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
  }
 }
 return exposedObject;
}

這個方法有一個參數 Bean,這個參數 Bean 會經過一些后置處理器處理之后返回,后置處理器主要是看一下這個 Bean 是否需要 AOP,如果需要就進行 AOP 處理,如果不需要,直接就把這個參數 Bean 返回就行了。至于這個參數是哪來的,我在 2.4 小節中已經加黑標記出來了,這個參數 Bean 其實就是原始的 A 對象!

好了,現在 B 對象就從緩存池中拿到了原始的 A 對象,B 對象屬性注入完畢,對象創建成功,進而導致 A 對象也創建成功。

大功告成。

3. 小結

老實說,如果小伙伴們認認真真看過松哥最近發的 Spring 源碼文章,今天的內容很好懂~至此,Spring 循環依賴,從思路到源碼,都和大家分析完畢了~感興趣的小伙伴可以 DEBUG 走一遍哦~

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

2023-11-28 08:00:00

SpringJava

2020-12-29 08:34:08

spring循環依賴開發

2022-08-17 07:52:31

Spring循環依賴單例池

2019-11-26 14:30:20

Spring循環依賴Java

2023-10-07 08:40:57

緩存屬性Spring

2022-09-09 07:35:23

循環依賴Spring構造器

2024-02-22 08:00:00

SoraOpenAI

2022-08-08 08:00:00

人工智能機器學習計算機應用

2019-05-28 13:50:27

MySQL幻讀數據庫

2022-05-24 17:00:41

區塊鏈IT比特幣

2023-10-11 12:35:29

Maven

2015-10-09 11:01:07

iPhone原創鎖定

2013-04-24 09:08:17

Google眼鏡

2022-08-12 08:03:59

算力網絡算力網絡

2010-04-02 16:46:43

云計算

2023-12-15 07:23:39

電子管半導體芯片集成電路

2020-06-22 08:07:48

Spring依賴場景

2010-03-11 14:15:24

Python循環

2016-08-02 14:53:18

Windows10藍屏死機

2022-03-02 21:53:42

數據中心運營互聯網
點贊
收藏

51CTO技術棧公眾號

国产精品aaaa| 日韩av最新在线| 欧美精品久久96人妻无码| 国产男女无套免费网站| 在线观看一区视频| 国产视频久久久| 午夜一区二区视频| 1234区中文字幕在线观看| 成人三级伦理片| 国产精品久久久久久亚洲影视| 欧美爱爱免费视频| 美女一区2区| 欧美美女一区二区| 91专区在线观看| 国产美女福利在线| 久久综合九色欧美综合狠狠| 91亚洲精华国产精华| 日本视频免费在线| 91成人网在线观看| 亚洲人成在线观看网站高清| 久久黄色一级视频| 久久三级毛片| 欧美午夜久久久| 成年在线观看视频| 日本在线免费网| 91蝌蚪国产九色| 国产91免费视频| 91国产免费视频| 日韩在线一区二区| 992tv在线成人免费观看| 欧美肥妇bbwbbw| 国产欧美日韩在线观看视频| 亚洲成人激情在线| 被黑人猛躁10次高潮视频| 亚洲不卡系列| 狠狠久久亚洲欧美专区| 国产资源在线免费观看| 精品视频在线一区二区| 国产午夜精品一区二区| 久久久久免费网| 四虎永久在线观看| 丰满放荡岳乱妇91ww| 亚洲已满18点击进入在线看片| 中文字幕在线视频第一页| 久久国产精品亚洲77777| 97在线视频免费播放| 久久久一二三区| 女人香蕉久久**毛片精品| 色狠狠av一区二区三区香蕉蜜桃| jizz中文字幕| 精品国产乱码久久久| 亚洲片国产一区一级在线观看| 第四色在线视频| 久久精品66| 日韩精品999| 亚洲一区二区三区四区五区六区| 欧美黑白配在线| 日韩av一区在线观看| 国产精品久久无码| 最近国产精品视频| 亚洲小视频在线| 纪美影视在线观看电视版使用方法| 欧美精品一区二区三区精品| 综合激情国产一区| 午夜精品一区二区三级视频| 久久久五月天| 欧美激情网站在线观看| 久草精品视频在线观看| 校园春色综合网| 国产精品久久久久av免费| 中文字幕视频在线播放| 极品美女销魂一区二区三区| 7777精品伊久久久大香线蕉语言| 亚洲国产精品suv| 91视频精品在这里| 日韩欧美一区二区视频在线播放| 在线播放麻豆| 亚洲黄色性网站| 日韩免费一级视频| 欧美成人精品三级网站| 欧美精品乱人伦久久久久久| 少妇高潮一69aⅹ| 日本妇女一区| 色吧影院999| 国产无码精品在线观看| 视频在线观看91| 91亚洲永久免费精品| 欧日韩在线视频| 国产日韩av一区| 国产免费一区二区三区四在线播放| 精品精品导航| 91高清视频免费看| 亚洲欧美激情一区二区三区| 日韩精选在线| 久久精品国产欧美亚洲人人爽| 久久久久久国产精品视频| 久久黄色网页| 91传媒在线免费观看| 你懂的视频在线免费| 专区另类欧美日韩| 免费无码国产v片在线观看| 国产情侣一区二区三区| 精品国产1区二区| 国产性猛交xx乱| 在线欧美视频| 成人网中文字幕| 亚洲人视频在线观看| 中文字幕在线不卡视频| 人妻少妇精品久久| 亚洲一区二区三区久久久| 日韩av影院在线观看| 2025国产精品自拍| 视频一区二区三区中文字幕| 成人蜜桃视频| 欧洲美女少妇精品| 色综合网站在线| 国产在线观看免费播放| 色小子综合网| 日韩美女在线播放| 四虎免费在线观看| 夜夜嗨av一区二区三区中文字幕| 一级特黄性色生活片| 欧美日韩一区二区三区在线电影| 久久手机免费视频| 久草视频在线免费| 久久久欧美精品sm网站| www精品久久| 亚洲午夜免费| 粗暴蹂躏中文一区二区三区| 最新中文字幕免费| 国产日本一区二区| 国产精品欧美激情在线观看| 国内精品免费| 欧美—级a级欧美特级ar全黄| 一级黄色大片免费| 中文幕一区二区三区久久蜜桃| 国产成人无码精品久久久性色| 涩爱av色老久久精品偷偷鲁| 久久精品视频网站| 97精品久久人人爽人人爽| 亚洲国产精品成人综合色在线婷婷| 日韩欧美精品在线观看视频| 婷婷国产精品| 欧美性资源免费| 天堂成人在线| 欧美午夜宅男影院在线观看| 午夜av免费看| 亚洲综合国产| 欧美性xxxx69| 欧美xnxx| 最新亚洲国产精品| 国产精品国产三级国产aⅴ| 日韩一区在线看| 久久久福利影院| 欧美激情第8页| yy111111少妇影院日韩夜片| 日本aa在线| 亚洲风情亚aⅴ在线发布| 国产一级视频在线| 99久久精品国产麻豆演员表| 欧美激情 国产精品| 亚州av日韩av| 国产极品jizzhd欧美| 国产中文字幕在线看| 欧美午夜免费电影| 青青操在线视频观看| 国产综合久久久久久久久久久久| 麻豆一区二区三区在线观看| 97人人澡人人爽91综合色| 欧美激情亚洲综合一区| 亚洲日本在线播放| 欧美色视频一区| 波多野结衣久久久久| 成人综合婷婷国产精品久久 | 国产手机视频一区二区 | 国产视频网址在线| 欧美日韩久久一区二区| 免费在线观看一级片| 99精品视频一区| 国产一级特黄a大片免费| 偷偷www综合久久久久久久| 国产高清精品一区二区三区| 老司机2019福利精品视频导航| 在线视频一区二区| 亚洲精华国产精华精华液网站| 欧美日韩国产中字| 国产成人免费在线观看视频| 成人午夜在线播放| 久久久精品麻豆| 精品电影一区| 午夜一区二区三区| 久本草在线中文字幕亚洲| 国产成人中文字幕| 国产探花在线观看| 伊人久久免费视频| 日韩中文字幕免费观看| 欧美三级蜜桃2在线观看| 亚洲国产综合久久| 中文字幕制服丝袜成人av| 中文字幕第3页| 精品亚洲成a人| 女性隐私黄www网站视频| 中文乱码免费一区二区三区下载| 欧美精品亚洲| 97一区二区国产好的精华液| 国产精品亚洲欧美导航| 天堂av中文在线观看| 另类图片亚洲另类| 国产精品综合久久久| 麻豆传媒在线免费看| 亚洲国产天堂久久国产91| 国产一区二区在线视频观看| 午夜激情综合网| 久热这里有精品| 日本一区二区三区高清不卡| 91黄色免费视频| 国产高清在线精品| 182午夜在线观看| 久久婷婷丁香| 日韩伦理在线免费观看| 欧美a级一区| 亚洲开发第一视频在线播放| 日韩成人动漫在线观看| 都市激情久久久久久久久久久| 另类一区二区三区| 日韩免费在线播放| 小早川怜子影音先锋在线观看| 欧美日韩国产成人在线观看| 黄视频网站在线| 中文字幕无线精品亚洲乱码一区| 青青草免费在线| 日韩av资源在线播放| 神马午夜在线观看| 欧美精品一区男女天堂| aa视频在线免费观看| 欧美老人xxxx18| 中文字幕乱码人妻二区三区| 日本高清不卡在线观看| 亚洲黄色免费观看| 欧美日韩免费看| 一级片中文字幕| 午夜欧美大尺度福利影院在线看 | 亚洲一区二区三区影院| 加勒比婷婷色综合久久| 亚洲欧美日韩国产一区二区三区| eeuss中文字幕| 欧美国产成人精品| 黄色激情小视频| 1区2区3区国产精品| 免费成人深夜夜行网站| 亚洲女爱视频在线| 久久精品一级片| 亚洲国产成人va在线观看天堂| 国产亚洲欧美精品久久久www| 一个色综合av| 国产精品第二十页| 色天使久久综合网天天| 成年人晚上看的视频| 欧美日韩另类一区| www.久久久久久| 亚洲成年人在线| 午夜福利一区二区三区| 亚洲欧美日韩中文在线制服| 国产区视频在线播放| 日韩中文第一页| 亚洲丝袜精品| 88国产精品欧美一区二区三区| 成人免费看黄| 成人中文字幕在线观看| 久久天堂久久| 久久久久久艹| 日韩精品一卡| 无码日本精品xxxxxxxxx| aa国产精品| 久久国产这里只有精品| 国产成人免费视频| 亚洲综合色一区| 综合网在线视频| 中文字幕日韩一级| 欧美日韩中文字幕一区| 亚洲精品久久久久久久久久 | 成人免费网址| 91精品国产91| 欧美aaaaaa| 国产欧美亚洲日本| jiujiure精品视频播放| 成人污网站在线观看| 男人的天堂亚洲| 日本中文字幕在线不卡| 91天堂素人约啪| 中国毛片直接看| 黑人巨大精品欧美一区二区一视频| 一区二区视频网| 亚洲福利在线播放| 四虎久久免费| 97色伦亚洲国产| 亚洲aⅴ网站| 久久资源亚洲| 欧美久久一级| 亚洲欧美视频二区| 91女神在线视频| 日韩高清dvd碟片| 欧美色道久久88综合亚洲精品| 国产尤物在线观看| 亚洲欧美日韩成人| 国产后进白嫩翘臀在线观看视频| 国产精品嫩草影院久久久| 高清精品视频| 欧美性视频在线播放| 久久一区激情| 中文乱码人妻一区二区三区视频| 亚洲欧洲日韩av| 欧美成人一区二区三区四区| 精品国产精品一区二区夜夜嗨| av在线资源网| 57pao成人永久免费视频| 中文字幕一区二区三区四区久久| 亚洲视频导航| 视频在线在亚洲| 免费看污黄网站在线观看| 亚洲一区在线观看网站| 国产精品污视频| 日韩中文字幕在线视频| 日韩中文在线播放| 日本婷婷久久久久久久久一区二区| 激情欧美丁香| 免费在线观看日韩av| 亚洲男人天堂av网| 亚洲视频中文字幕在线观看| 国产亚洲在线播放| 粉嫩一区二区三区| 欧美一区二区三区在线免费观看| 在线成人h网| 亚洲精品乱码久久久久久蜜桃欧美| 亚洲图片欧美激情| 97人妻精品一区二区三区| 一区二区三区精品99久久 | 亚洲欧洲一级| 成人做爰www看视频软件| 一区二区三区蜜桃网| 国产毛片毛片毛片毛片| 日韩专区在线观看| 亚洲高清影院| 日本xxxxx18| 国产不卡视频在线观看| 美国黄色小视频| 日韩欧美国产综合在线一区二区三区 | 国产福利91精品一区| 黄色一级片在线| 精品国产乱码久久久久久久久 | 欧美亚洲综合在线| 成人免费在线视频网| 国产精品视频自在线| 久久综合电影| 伊人精品视频在线观看| 自拍偷拍国产亚洲| 黄色av网址在线| 欧美在线免费观看| 极品美女一区二区三区| 天天综合网久久| 亚洲乱码日产精品bd| 亚洲乱码在线观看| 欧美一性一乱一交一视频| 精品国产91乱码一区二区三区四区| 向日葵污视频在线观看| 亚洲精品一卡二卡| 天堂在线资源库| 国产精品精品国产| 影视一区二区| 加勒比精品视频| 欧美三级三级三级爽爽爽| 日日夜夜天天综合入口| 狠狠色综合网站久久久久久久| 久久伊人亚洲| 老熟妇高潮一区二区三区| 亚洲高清色综合| 播放一区二区| 一二三四中文字幕| 91麻豆免费看| 97超碰人人模人人人爽人人爱| 欧美激情在线视频二区| 免费精品国产| 国产伦精品一区二区三区妓女下载| 午夜精品久久久久| а√天堂中文在线资源bt在线| 亚洲影院在线看| 久久亚洲一区| 妺妺窝人体色www在线下载| 亚洲男人的天堂在线| 精品亚洲二区| 欧美精品无码一区二区三区| 亚洲蜜臀av乱码久久精品| 你懂的在线免费观看| 97超碰人人看人人| 久久婷婷久久| 青娱乐国产在线视频| 伊人久久男人天堂| 精品人人人人| 波多野结衣中文字幕在线播放| 色老汉一区二区三区|