一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 深入理解Java的Spring框架中的IOC容器

深入理解Java的Spring框架中的IOC容器

2020-05-26 11:49JinhaoPlus JAVA教程

IOC(Inversion of Control,控制反轉)是Spring框架的核心,負責控制對象的生命周期與關系,接下來就讓我們跟隨文章來深入理解Java的Spring框架中的IOC容器:

Spring IOC的原型
spring框架的基礎核心和起點毫無疑問就是IOC,IOC作為spring容器提供的核心技術,成功完成了依賴的反轉:從主類的對依賴的主動管理反轉為了spring容器對依賴的全局控制。

這樣做的好處是什么呢?

當然就是所謂的“解耦”了,可以使得程序的各模塊之間的關系更為獨立,只需要spring控制這些模塊之間的依賴關系并在容器啟動和初始化的過程中將依據這些依賴關系創建、管理和維護這些模塊就好,如果需要改變模塊間的依賴關系的話,甚至都不需要改變程序代碼,只需要將更改的依賴關系進行修改即可,spring會在再次啟動和初始化容器的過程中使得這些新的依賴關系重新建立符合新需求的模塊,在這個過程中,需要注意的是代碼本身不需要體現對于模塊具體依賴情形的聲明而只需要定義其所需模塊的接口,所以這是一種典型的面向接口思想,同時最好將依賴關系以配置文件或者注解的形式表述出來,相關的spring處理類會根據這些外部的配置文件組裝模塊,或者掃描注解調用內部的注解處理器組裝模塊,以此完成IOC的過程。

IOC的目的是稱為DI的依賴注入,通過IOC技術,最終容器將幫助我們完成模塊間的依賴注入。

另外,最終的一點是,在spring IOC的過程中,我們必須始終清楚以上這條主線,即時語法和類的結構再復雜,但是其作用和目的都是一樣的:就是通過依賴描述的配置文件這一裝配“圖紙”去完成模塊的“組裝”,復雜的語法只是完成這一目的的手段罷了。

所謂的IOC原型,為了展示最簡單的IOC原理圖,我們不妨做一個完全簡單的原型來說明這個過程:

首先是我們定義的幾個模塊,包括主模塊和兩個接口定義的依賴模塊:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class MainModule{
  private DependModuleA moduleA;
  private DependModuleB moduleB;
  public DependModuleA getModuleA() {
    return moduleA;
  }
  public void setModuleA(DependModuleA moduleA) {
    this.moduleA = moduleA;
  }
  public DependModuleB getModuleB() {
    return moduleB;
  }
  public void setModuleB(DependModuleB moduleB) {
    this.moduleB = moduleB;
  }
  
}
 
interface DependModuleA{
  public void funcFromModuleA();
}
 
interface DependModuleB{
  public void funcFromModuleB();
}
 
class DependModuleAImpl implements DependModuleA{
 
  @Override
  public void funcFromModuleA() {
    System.out.println("This is func from Module A");
  }
  
}
 
class DependModuleBImpl implements DependModuleB{
 
  @Override
  public void funcFromModuleB() {
    System.out.println("This is func from Module B");
  }
  
}

如果我們不采用IOC,而是依靠主模塊本身去控制其依賴模塊的創建,那么會是這樣的:

?
1
2
3
4
5
6
7
8
9
public class SimpleIOCDemo {
  public static void main(String[] args) throws ClassNotFoundException {
    MainModule mainModule = new MainModule();
    mainModule.setModuleA(new DependModuleAImpl());
    mainModule.setModuleB(new DependModuleBImpl());
    mainModule.getModuleA().funcFromModuleA();
    mainModule.getModuleB().funcFromModuleB();
  }
}

這是我們經過簡化定義的IOC容器原型,容器在啟動后初始化的時候會讀取用戶寫入的配置文件,這里我們以簡單的properties配置文件為例,只有當用戶調取getBean方法的時候才會真正地按照配置文件組裝加載相應的bean,在我們定義的容器原型內部維護著一個用于保存裝配好的bean 的map,如果在其中有滿足要求的bean的話就不需要再新建了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class SimpleIOCContainer{
  private Properties properties = new Properties();
  private Map<String, Object> moduleMap = new HashMap<>();
  {
    try {
      properties.load(new FileInputStream(new File("SimpleIOC.properties")));
    } catch (Exception e) {
      e.printStackTrace();
    }
    
  }
  public Object getBean(String moduleName) throws ClassNotFoundException {
    Object instanceObj;
    if(moduleMap.get(moduleName)!=null){
      System.out.println("return old bean");
      return moduleMap.get(moduleName);
    }
    System.out.println("create new bean");
    String fullClassName = properties.getProperty(moduleName);
    if(fullClassName == null)
      throw new ClassNotFoundException();
    else{
      Class<? extends Object> clazz = Class.forName(fullClassName);
      try {
        instanceObj = clazz.newInstance();
        instanceObj = buildAttachedModules(moduleName,instanceObj);
        moduleMap.put(moduleName, instanceObj);
        return instanceObj;
      } catch (InstantiationException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      }
    }
    return null;
  }
  private Object buildAttachedModules(String modulename , Object instanceObj) {
    Set<String> propertiesKeys = properties.stringPropertyNames();
    Field[] fields = instanceObj.getClass().getDeclaredFields();
    for (String key : propertiesKeys) {
      if(key.contains(modulename)&&!key.equals(modulename)){
        try {
          Class<? extends Object> clazz = Class.forName(properties.getProperty(properties.getProperty(key)));
          for (Field field : fields) {
            if(field.getType().isAssignableFrom(clazz))
              field.set(instanceObj, clazz.newInstance());
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
        
      }
    }
    return instanceObj;
  }
}

這是我們使用properties配置文件寫成的依賴關系配置文件,這個配置文件是我們裝配模塊的“圖紙”,這里的語法個是完全是我們定義的,在真實的spring IOC容器中,為了表達更為復雜的依賴邏輯,會使用更為發達的xml格式配置文件或者更新的注解配置,依靠注解處理器來完成圖紙的解析:

?
1
2
3
4
5
mainModule=com.rocking.demo.MainModule
mainModule.moduleA=moduleA
mainModule.moduleB=moduleB
moduleA=com.rocking.demo.DependModuleAImpl
moduleB=com.rocking.demo.DependModuleBImpl

這是測試代碼,可以看到的是我們可以完整的通過我們定義的IOC容器獲取到符合要求的模塊,同時也可以發現我們定義的容器可以為我們維護這些bean,當有bean已經組裝創建出來之后就不需要再創建了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SimpleIOCDemo {
  public static void main(String[] args) throws ClassNotFoundException {
    SimpleIOCContainer container = new SimpleIOCContainer();
    DependModuleA moduleA = (DependModuleA) container.getBean("moduleA");
    moduleA.funcFromModuleA();
    DependModuleB moduleB = (DependModuleB) container.getBean("moduleB");
    moduleB.funcFromModuleB();
    MainModule mainModule = (MainModule) container.getBean("mainModule");
    mainModule.getModuleA().funcFromModuleA();
    mainModule.getModuleB().funcFromModuleB();
    container.getBean("mainModule");
  }
}

這就是我依據IOC的基本思想創建的IOC容器原型,spring IOC雖然語法復雜,但是說到底完成的任務在核心上都是一樣的,所謂的“萬變不離其宗”。

 

Spring IOC 的具體過程
上回展示了IOC的大致實現的原型,那么在Spring框架中具體是怎么實現這個容器根據metadata元信息配置加載POJO的過程的呢?在整個Spring IOC容器的工作過程中有很多地方是設計地相當靈活的,供給使用者很多空間去完成自己的任務,而不是一味地只是完成容器的機械過程。

這是整個IOC容器工作過程的過程圖:

深入理解Java的Spring框架中的IOC容器

 

1、容器啟動階段
(1)加載配置文件信息
(2)解析配置文件信息
(3)裝配BeanDefinition
(4)后處理
首先配置文件或者注解等元信息和JavaBean的類信息被加載到IOC容器中,容器讀取到xml格式的配置文件,這個配置文件是使用者聲明的依賴關系和裝配中需要特別關注的地方,是裝配Bean的早期“外部圖紙”,容器中的解析引擎可以把我們寫入的文本形式的字符元信息解析成容器內部可以識別的BeanDefinition,可以把BeanDefinition理解成為類似反射機制的類結構,這個通過對JavaBean和配置文件進行分析得到的BeanDefinition獲取了組裝一個符合要求的JavaBean的基本結構,如果需要除了BeanDefinition之后還要對這個BeanDefinition再做修改的話則執行這個后處理,后處理一般是通過Spring框架內的BeanFactoryPostProcessor處理的。

我們仍然使用上次使用過的例子來說明這個BeanDefinition的運作原理:有三個bean,主模塊MainModule和依賴模塊DependModuleA,DependModuleB,前者依賴后面兩個模塊構成,在配置文件里我們一般會這么進行依賴的聲明:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
  <bean id="mainModule" class="com.rocking.demo.MainModule">
    <property name="moduleA">
      <ref bean="moduleA"/>
    </property>
    <property name="moduleB">
      <ref bean="moduleB"/>
    </property>
  </bean>
  <bean id="moduleA" class="com.rocking.demo.DependModuleAImpl"></bean>
  <bean id="moduleB" class="com.rocking.demo.DependModuleBImpl"></bean>
</beans>

這是我們的程序演示一個標準的BeanFactory容器(Spring IOC容器的實現之一)對上面配置文件的裝配:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class MainModule {
  private DependModuleA moduleA;
  private DependModuleB moduleB;
 
  public DependModuleA getModuleA() {
    return moduleA;
  }
 
  public void setModuleA(DependModuleA moduleA) {
    this.moduleA = moduleA;
  }
 
  public DependModuleB getModuleB() {
    return moduleB;
  }
 
  public void setModuleB(DependModuleB moduleB) {
    this.moduleB = moduleB;
  }
 
}
 
interface DependModuleA {
  public void funcFromModuleA();
}
 
interface DependModuleB {
  public void funcFromModuleB();
}
 
class DependModuleAImpl implements DependModuleA {
 
  @Override
  public void funcFromModuleA() {
    System.out.println("This is func from Module A");
  }
 
}
 
class DependModuleBImpl implements DependModuleB {
 
  @Override
  public void funcFromModuleB() {
    System.out.println("This is func from Module B");
  }
 
}
 
public class SimpleIOCDemo {
  public static void main(String[] args) throws ClassNotFoundException {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("Beans.xml");
    MainModule mainModule = (MainModule) beanFactory.getBean("mainModule");
    mainModule.getModuleA().funcFromModuleA();
    mainModule.getModuleB().funcFromModuleB();
  }
}

這里我們的配置文件和JavaBean被加載讀取并被解析,這里的BeanDefinition生成使用過程掩藏在其中,這是實際上在IOC內部發生的大致過程:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SimpleIOCDemo {
  public static void main(String[] args) throws ClassNotFoundException {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    AbstractBeanDefinition mainModule = new RootBeanDefinition(MainModule.class);
    AbstractBeanDefinition moduleA = new RootBeanDefinition(DependModuleAImpl.class);
    AbstractBeanDefinition moduleB = new RootBeanDefinition(DependModuleBImpl.class);
    
    beanFactory.registerBeanDefinition("mainModule", mainModule);
    beanFactory.registerBeanDefinition("moduleA", moduleA);
    beanFactory.registerBeanDefinition("moduleB", moduleB);
    
    MutablePropertyValues propertyValues = new MutablePropertyValues();
    propertyValues.add("moduleA", moduleA);
    propertyValues.add("moduleB", moduleB);
    mainModule.setPropertyValues(propertyValues);
    
    MainModule module = (MainModule) beanFactory.getBean("mainModule");
    module.getModuleA().funcFromModuleA();
    module.getModuleB().funcFromModuleB();
  }
}

對xml的元信息進行加載讀取后,IOC解析引擎會將其中提到的模塊依據其真實類型創建成BeanDefinition,這個BeanDefinition可以看成是一種反射或者代理的過程,目的是為了讓IOC容器清楚以后要創建的實例對象的bean結構,然后將這些bean結構注冊到BeanFactory中去,之后將主模塊的依賴以setter注入的形式加入到主模塊的屬性中去,(這一點要看主模塊提供的是setter方法還是初始化方法),這個過程結束后注冊完所有“圖紙”上規定的bean的Definition后,BeanFactory就已經成型。之后只要調用getBean方法即可將符合要求的bean生產出來,這是下一階段的過程,我們之后再說。

在將BeanDefinition這一“圖紙”上的信息注冊到BeanFactory完畢后,我們仍然可以對已經注冊完的BeanDefinition進行改動的操作,這就是我們前面提到的Spring為使用者設計的靈活的地方之一,不是說所有的過程不可控,而是在很多地方留了很多使用者可以發揮的余地。具體的辦法是使用BeanFactory處理器BeanFactoryPostProcessor來介入對BeanFactory的處理以進一步改寫我們需要修改的BeanDefinition部分。這個過程對應流程里的“后處理”過程。
以常見的處理器之一:屬性占位符配置處理器為例,就是在已經構建完成已注冊完畢的BeanFactory之后再對它處理,以使得BeanDefinition相應屬性里的內容修改為配置處理器指定配置文件里的信息:

?
1
2
3
4
5
6
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions( new ClassPathResource( "Beans.xml"));
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
configurer.setLocation( new ClassPathResource( "about.properties"));
configurer.postProcessBeanFactory( beanFactory);

BeanFactoryPostProcessor將對BeanFactory處理,處理的結果就是把BeanDefinition中定義的某些屬性改成BeanFactoryPostProcessor定義位置處的某些信息。

2、Bean 實例化階段
有了經過處理的BeanDefinition的“內部圖紙”的指導下,容器可以進一步把BeanDefifnition通過反射或CGLIB動態字節碼生產的方式化為存在于內存中的活化實例對象,再將BeanDefinition規定的依賴對象通過setter注入或者初始化注入的方式裝配進新創建的實例對象中,這里是實實在在地將依賴對象的引用賦給需要依賴的對象屬性中。
但是這里需要注意的是創建的實例不僅僅是一個簡單的bean定義的實例,而是一個經過Spring包裝的BeanWrapper實例,這里為什么要采用BeanWrapper的方式來包裝bean呢?是因為BeanWrapper提供了統一訪問bean屬性的接口,在創建完了基本的bean的框架后要對其中的屬性進行設置,每個bean的setter方法都不一樣,所以如果直接用反射設置的話會非常復雜,所以spring提供這種包裝來簡化屬性設置:

?
1
2
3
4
5
6
BeanWrapper beanWrapper = new BeanWrapperImpl(Class.forName("com.rocking.demo.MainModule"));
beanWrapper.setPropertyValue( "moduleA", Class.forName("com.rocking.demo.DepModuleAImpl").newInstance());
beanWrapper.setPropertyValue( "moduleB", Class.forName("com.rocking.demo.DepModuleBImpl").newInstance());
MainModule mainModule= (MainModule) beanWrapper.getWrappedInstance();
mainModule.getModuleA().funcFromA();
mainModule.getModuleB().funcFromB();

以上的過程展示了在Spring內部,通過獲取類的反射容器了解將來包裝的實例bean的結構并作出包裝,使用統一的屬性設置方法setPropertyValue來對這個包裝的實例設置屬性,最后得到的bean實例通過getWrappedInstance拿到,可以發現已經成功將其屬性賦值。

這個時候的bean實例其實已經完全可以使用了,但是Spring同樣在實例化階段也為我們準備了靈活的策略以完成使用者對這個階段的介入,和容器啟動階段的BeanFactoryPostProcessor控制BeanDefinition類似,在實例化階段,Spring提供了BeanPostProcessor處理器來對已經裝配好的實例進行操作,以完成可能需要的改動:、

這里舉個例子來說明,定義一個BeanPostProcessor的實現類,實現其中的方法postProcessAfterInitialization和postProcessBeforeInitialization來定義對在bean實例裝配之后和之前分別進行的操作,在BeanFactory添加了這個處理器后就會在每次調用getBean方法裝配實例的時候,都會傳入根據“圖紙”裝配出的bean實例(包括裝配過程中創建的依賴實例bean)調用這兩個方法,這些方法可以對這些bean實例實施修改。

下面是一個這樣的例子(MainModule及其依賴關系和本文之前的例子相同):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class ModuleC {
  private String x;
 
  public String getX() {
    return x;
  }
 
  public void setX(String x) {
    this.x = x;
  }
  
}
 
class ModulePostProcessor implements BeanPostProcessor{
 
  @Override
  public Object postProcessAfterInitialization(Object object, String string)
      throws BeansException {
    System.out.println(string);
    if(object instanceof ModuleC){
      System.out.println(string);
      ((ModuleC)object).setX("after");
    }
    return object;
  }
 
  @Override
  public Object postProcessBeforeInitialization(Object object, String string)
      throws BeansException {
    if(object instanceof ModuleC){
      ((ModuleC)object).setX("before");
    }
    return object;
  }
  
}
 
public class VerySimpleIOCKernal {
  public static void main(String[] args) throws ClassNotFoundException, BeansException, InstantiationException, IllegalAccessException {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions(new ClassPathResource("Beans.xml"));
    ModulePostProcessor postProcessor = new ModulePostProcessor();
    beanFactory.addBeanPostProcessor(postProcessor);
    MainModule module = (MainModule) beanFactory.getBean("mainModule");
    ModuleC moduleC = (ModuleC) beanFactory.getBean("moduleC"); 
    System.out.println(moduleC.getX());
  }
}

這是bean的依賴關系配置文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="mainModule" class="com.rocking.demo.MainModule">
    <property name="moduleA">
      <ref bean="moduleA"/>
    </property>
    <property name="moduleB">
      <ref bean="moduleB"/>
    </property>
  </bean>
  <bean id="moduleA" class="com.rocking.demo.DepModuleAImpl">
    <property name="infoA">
      <value>${moduleA.infoA}</value>
    </property>
  </bean>
  <bean id="moduleB" class="com.rocking.demo.DepModuleBImpl">
    <property name="infoB">
      <value>info of moduleB</value>
    </property>
  </bean>
  <bean id="moduleC" class="com.rocking.demo.ModuleC">
  </bean>
</beans>

從最終的結果我們可以看出,每次調用getBean方法得到的bean實例(包括因依賴關系生成的)都將被BeanPostProcessor獲取進行前置和后置處理。

除了類似上面的BeanPostProcessor的辦法對裝配好的bean再做處理外,Spring還可以通過配置init-method和destroy-method來對bean的初始化和銷毀過程設置回調函數,這些回調函數也還可以靈活地提供更改bean實例的機會。

整個Spring IOC的過程其實總體來說和我們自己寫的IOC原型在本質上是一樣的,只不過通過復雜的設計使得IOC的過程能夠更靈活有效地提供給使用者更多的發揮空間,除此之外,Spring的IOC也在安全性、容器的穩定性、metadata到bean轉換的高效性上做到了精美的設計,使得IOC這一Spring容器的基礎得以穩固。

延伸 · 閱讀

精彩推薦
  • JAVA教程Java枚舉類用法實例

    Java枚舉類用法實例

    這篇文章主要介紹了Java枚舉類用法,實例分析了java中枚舉類的實現與使用技巧,需要的朋友可以參考下 ...

    wo_soul2592019-12-19
  • JAVA教程java編程實現郵件定時發送的方法

    java編程實現郵件定時發送的方法

    這篇文章主要介紹了java編程實現郵件定時發送的方法,涉及Java基于定時器實現計劃任務的相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下 ...

    awj35841662020-01-15
  • JAVA教程深入探討JAVA中的異常與錯誤處理

    深入探討JAVA中的異常與錯誤處理

    這篇文章詳細介紹了JAVA中的異常與錯誤處理,有需要的朋友可以參考一下 ...

    java技術網3372019-10-14
  • JAVA教程用Java實現希爾排序的示例

    用Java實現希爾排序的示例

    問題:現有一段程序S,可以對任意n個數進行排序。如果現在需要對n^2個數進行排序,最少需要調用S多少次?只允許調用S,不可以做別的操作。我們用希爾...

    java教程網3982019-10-20
  • JAVA教程App登陸java后臺處理和用戶權限驗證

    App登陸java后臺處理和用戶權限驗證

    這篇文章主要為大家詳細介紹了App登陸java后臺處理和用戶權限驗證,感興趣的朋友可以參考一下 ...

    夢想實現家-Tony4812020-05-13
  • JAVA教程Java里的static import使用小結

    Java里的static import使用小結

    這篇文章主要介紹了Java里的static import使用小結,本文給出了一些使用示例,并總結了一些使用特性,需要的朋友可以參考下 ...

    junjie1272019-12-21
  • JAVA教程java生成申請單序列號的實現方法

    java生成申請單序列號的實現方法

    申請單序列號一般要求根據一定的規則生成后幾位連續的字符串,下面是我項目中使用的生成序列號的代碼,其中用到了鎖機制,有需要的朋友可以參考一...

    java開發網5032019-10-27
  • JAVA教程java觀感示例分享

    java觀感示例分享

    這篇文章主要介紹了java觀感示例,該實例查詢并生成了系統中存在觀感對應的按鈕并在用戶點擊相應按鈕時將窗口的觀感切換到指定的觀感上 ...

    java技術網1282019-11-12
主站蜘蛛池模板: 成人青青草 | 日韩色综合 | 欧美日韩国产一区二区三区伦 | 国产在线91 | 69堂最新地域网名 | 色天天色综合 | 色婷婷综合久久久中文字幕 | 国产午夜亚洲精品理论片不卡 | 满溢游泳池免费土豪全集下拉版 | 美日韩在线观看 | 日本一区二区三区在线 视频 | 百合互慰吃奶互揉漫画 | 999久久精品国产 | 1024亚洲天堂 | 婷婷色在线 | 精品丰满人妻无套内射 | 波多野结衣178部中文字幕 | 国产高清在线精品一区二区三区 | 午夜理论片日本中文在线 | 精品国产剧情在线观看 | 国内揄拍国内精品久久 | 免费的伦理片 | 日本黄视频在线播放 | 亚洲色图亚洲色图 | 亚洲国产精品成人久久 | 美女班主任让我爽了一夜视频 | 日韩人成 | 日本mv精品中文字幕 | 国产成人综合久久精品红 | 久久亚洲精品AV成人无码 | 草莓污污 | 拍拍叫痛的无挡视频免费 | 精品一区二区三区五区六区七区 | 日韩一级生活片 | 好大好爽好硬 | 国产日产在线观看 | 美女认你摸 | 国产精品永久免费视频 | 视频污版| 思思玖玖 | 日本videohd18|