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

從頭捋了一遍 Java 代理機制,收獲頗豐

開發 后端
前文提到,動態代理機制使用了反射,Spring 中的 AOP 由于使用了動態代理,所以也相當于使用了反射機制。

[[385110]]

本文轉載自微信公眾號「飛天小牛肉」,作者飛天小牛肉。轉載本文請聯系飛天小牛肉公眾號。

❝這篇文章應該在反射那篇結束后就發出來的,結果搞忘了,現在補上。❞

前文提到,動態代理機制使用了反射,Spring 中的 AOP 由于使用了動態代理,所以也相當于使用了反射機制。那么,代理是什么?動態代理又是什么?動態代理中是如何使用反射的?全文脈絡思維導圖如下:


 

 

1. 常規編碼方式在學習代理之前,先回顧以下我們的常規編碼方式:所有 interface 類型的變量總是通過向上轉型并指向某個實例的。

1)首先,定義一個接口:

  1. public interface SmsService { 
  2.     String send(String message); 

2)然后編寫其實現類:

  1. public class SmsServicseImpl implements SmsService { 
  2.     public String send(String message) { 
  3.         System.out.println("send message:" + message); 
  4.         return message; 
  5.     } 

3)最后創建該實現類的實例,轉型為接口并調用:

  1. SmsService s = new SmsServicseImpl(); 
  2. s.send("Java"); 

上述這種方式就是我們通常編寫代碼的方式。而代理模式和這種方式有很大的區別,且看下文。

2. 代理模式概述

簡單來說,代理模式就是 「使用代理對象來代替對真實對象的訪問,這樣就可以在不修改原目標對象的前提下,提供額外的功能操作,擴展目標對象的功能。」

代理模式大致有三種角色:

  • Real Subject:真實類,也就是被代理類、委托類。用來真正完成業務服務功能;
  • Proxy:代理類。將自身的請求用 Real Subject 對應的功能來實現,代理類對象并不真正的去實現其業務功能;
  • Subject:定義 RealSubject 和 Proxy 角色都應該實現的接口。

 

通俗來說,「代理模式的主要作用是擴展目標對象的功能,比如說在目標對象的某個方法執行前后你可以增加一些額外的操作,并且不用修改這個方法的原有代碼」。如果大家學過 Spring 的 AOP,一定能夠很好的理解這句話。

舉個例子:你找了小紅來幫你向小綠問話,小紅就看作是代理我的代理類 Proxy,而你是 Real Subject,因為小紅要傳達的話其實是你說的。那么你和小紅都需要實現的接口(Subject)就是說話,由于你倆都能說話,在外界看來你倆就是一樣的(滑稽,大家理解就好,不用較真)

 

看到這里,不知道大家能不能理解了為什么委托類和代理類都需要實現相同的接口?

那是為了保持行為的一致性,在訪問者看來兩者之間就沒有區別。這樣,通過代理類這個中間層,很好地隱藏和保護了委托類對象,能「有效屏蔽外界對委托類對象的直接訪問」。同時,也可以在代理類上加上額外的操作,比如「小紅在說話之前會跳一段舞,外界就會覺得你在說話前會跳一段舞,所以,這就實現了委托類的功能增強」。

代理模式有靜態代理和動態代理兩種實現方式。

3. 靜態代理

什么是靜態代理

先來看靜態代理的實現步驟:

1)定義一個接口(Subject)

2)創建一個委托類(Real Subject)實現這個接口

3)創建一個代理類(Proxy)同樣實現這個接口

4)「將委托類 Real Subject 注入進代理類 Proxy」,在代理類的方法中調用 Real Subject 中的對應方法。這樣的話,我們就可以通過代理類屏蔽對目標對象的訪問,并且可以在目標方法執行前后做一些自己想做的事情。

從實現和應用角度來說,靜態代理中,我們對目標對象的每個方法的增強都是手動完成的,非常不靈活(比如接口一旦新增加方法,目標對象和代理對象都要進行修改)且麻煩(需要對每個目標類都單獨寫一個代理類)。實際應用場景非常非常少,日常開發幾乎看不到使用靜態代理的場景。

從 JVM 層面來說, 「靜態代理在編譯時就將接口、委托類、代理類這些都變成了一個個實際的 .class 文件。」

代碼示例

1)定義發送短信的接口

  1. public interface SmsService { 
  2.     String send(String message); 

2)創建一個委托類(Real Subject)實現這個接口

  1. public class SmsServiceImpl implements SmsService { 
  2.     public String send(String message) { 
  3.         System.out.println("send message:" + message); 
  4.         return message; 
  5.     } 

3)創建一個代理類(Proxy)同樣實現這個接口

4)將委托類 Real Subject 注入進代理類 Proxy,在代理類的方法中調用 Real Subject 中的對應方法。這樣的話,我們就可以通過代理類屏蔽對目標對象的訪問,并且可以在目標方法執行前后做一些自己想做的事情。

  1. public class SmsProxy implements SmsService { 
  2.   
  3.     // 將委托類注入進代理類 
  4.     private final SmsService smsService; 
  5.  
  6.     public SmsProxy(SmsService smsService) { 
  7.         this.smsService = smsService; 
  8.     } 
  9.  
  10.     @Override 
  11.     public String send(String message) { 
  12.         // 調用委托類方法之前,我們可以添加自己的操作 
  13.         System.out.println("before method send()"); 
  14.         // 調用委托類方法 
  15.         smsService.send(message);  
  16.         // 調用委托類方法之后,我們同樣可以添加自己的操作 
  17.         System.out.println("after method send()"); 
  18.         return null
  19.     } 

那么,如何使用這個被增強的 send 方法呢?

  1. public class Main { 
  2.     public static void main(String[] args) { 
  3.         SmsService smsService = new SmsServiceImpl(); 
  4.         SmsProxy smsProxy = new SmsProxy(smsService); 
  5.         smsProxy.send("Java"); 
  6.     } 

運行上述代碼之后,控制臺打印出:

  1. before method send() 
  2. send message:java 
  3. after method send() 

從輸出結果可以看出,我們已經增強了委托類 SmsServiceImpl 的 send() 方法。

當然,從上述代碼我們也能看出來,靜態代理存在一定的弊端。假如說我們現在新增了一個委托類實現了 SmsService 接口,如果我們想要對這個委托類進行增強,就需要重新寫一個代理類,然后注入這個新的委托類,非常不靈活。也就是說靜態代理是一個委托了對應一個代理類,能不能「將代理類做成一個通用的」呢?為此,動態代理應用而生。

4. Java 字節碼生成框架

在講解動態之前,我們有必要詳細說一下 .class 字節碼文件這個東西。動態代理機制和 Java 字節碼生成框架息息相關。

在上文反射中我們提到,一個 Class 類對應一個 .class 字節碼文件,也就說字節碼文件中存儲了一個類的全部信息。字節碼其實是二進制文件,內容是只有 JVM 能夠識別的機器碼。

解析過程這樣的:JVM 讀取 .class 字節碼文件,取出二進制數據,加載到內存中,解析字節碼文件內的信息,生成對應的 Class 類對象:

 

顯然,上述這個過程是在編譯期就發生的。

那么,由于JVM 是通過 .class 字節碼文件(也就是二進制信息)加載類的,如果我們在運行期遵循 Java 編譯系統組織 .class 字節碼文件的格式和結構,生成相應的二進制數據,然后再把這個二進制數據加載轉換成對應的類。這樣,我們不就完成了在運行時動態的創建一個類。這個思想其實也就是動態代理的思想。

 

在運行時期按照 JVM 規范對 .class 字節碼文件的組織規則,生成對應的二進制數據。當前有很多開源框架可以完成這個功能,如

  • ASM
  • CGLIB
  • Javassist
  • ......

需要注意的是,「CGLIB 是基于 ASM 的」。這里簡單對比一下 ASM 和 Javassist:

  • Javassist 源代碼級 API 比 ASM 中實際的字節碼操作更容易使用
  • Javassist 在復雜的字節碼級操作上提供了更高級別的抽象層。Javassist 源代碼級 API 只需要很少的字節碼知識,甚至不需要任何實際字節碼知識,因此實現起來更容易、更快。
  • Javassist 使用反射機制,這使得它比 ASM 慢。

「總的來說 ASM 比 Javassist 快得多,并且提供了更好的性能,但是 Javassist 相對來說更容易使用」,兩者各有千秋。

以 Javassist 為例,我們來看看這些框架在運行時生成 .class 字節碼文件的強大能力。

正常來說,我們創建一個類的代碼是這樣的:

  1. package com.samples; 
  2.  
  3. public class Programmer { 
  4.  public void code(){ 
  5.   System.out.println("I'm a Programmer,Just Coding....."); 
  6.  } 

下面通過 Javassist 創建和上面一模一樣的 Programmer 類的字節碼:

  1. import javassist.ClassPool; 
  2. import javassist.CtClass; 
  3. import javassist.CtMethod; 
  4. import javassist.CtNewMethod; 
  5.  
  6. public class MyGenerator { 
  7.  public static void main(String[] args) throws Exception { 
  8.   ClassPool pool = ClassPool.getDefault(); 
  9.     // 創建 Programmer 類   
  10.   CtClass cc= pool.makeClass("com.samples.Programmer"); 
  11.   // 定義方法 
  12.   CtMethod method = CtNewMethod.make("public void code(){}", cc); 
  13.   // 插入方法代碼 
  14.   method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");"); 
  15.   cc.addMethod(method); 
  16.   // 保存生成的字節碼 
  17.   cc.writeFile("d://temp"); 
  18.  } 

通過反編譯工具打開 Programmer.class 可以看到以下代碼:

 

恐怖如斯!

5. 什么是動態代理OK,了解了 Java 字節碼生成框架,可以開始學習動態代理(Dynamic Proxy)了。

回顧一下靜態代理,我們把靜態代理的執行過程抽象為下圖:

 

可以看見,代理類無非是在調用委托類方法的前后增加了一些操作。委托類的不同,也就導致代理類的不同。

那么為了做一個通用性的代理類出來,我們把調用委托類方法的這個動作抽取出來,把它封裝成一個通用性的處理類,于是就有了動態代理中的 InvocationHandler角色(處理類)。

于是,在代理類和委托類之間就多了一個處理類的角色,這個角色主要是「對代理類調用委托類方法的這個動作進行統一的調用」,也就是由 InvocationHandler 來統一處理代理類調用委托類方法這個操作。看下圖:

 

「從 JVM 角度來說,動態代理是在運行時動態生成 .class 字節碼文件 ,并加載到 JVM 中的」。這個我們在 Java 字節碼生成框架中已經提到過。

雖然動態代理在我們日常開發中使用的相對較少,但是在框架中的幾乎是必用的一門技術。學會了動態代理之后,對于我們理解和學習各種框架的原理也非常有幫助,「Spring AOP、RPC 等框架的實現都依賴了動態代理」。

就 Java 來說,動態代理的實現方式有很多種,比如:

  • JDK 動態代理
  • CGLIB 動態代理
  • Javassit 動態代理
  • ......

下面詳細講解這三種動態代理機制。

6. JDK 動態代理機制

使用步驟

先來看下 JDK 動態代理機制的使用步驟:

1)定義一個接口(Subject)

2)創建一個委托類(Real Subject)實現這個接口

3)創建一個處理類并實現 InvocationHandler 接口,重寫其 invoke 方法(在invoke 方法中利用反射機制調用委托類的方法,并自定義一些處理邏輯),并將委托類注入處理類

 

該方法有下面三個參數:

  • proxy:代理類對象(見下一步)
  • method:還記得我們在上篇文章反射中講到的 Method.invoke 嗎?就是這個,我們可以通過它來調用委托類的方法(反射)

 

  • args:傳給委托類方法的參數列表

4)創建代理對象(Proxy):通過 Proxy.newProxyInstance() 創建委托類對象的代理對象

 

這個方法需要 3 個參數:

  • 類加載器 ClassLoader
  • 委托類實現的接口數組,至少需要傳入一個接口進去
  • 調用的 InvocationHandler 實例處理接口方法(也就是第 3 步我們創建的類的實例)

也就是說:我們在通過 Proxy 類的 newProxyInstance() 創建的代理對象在調用方法的時候,實際會調用到實現了 InvocationHandler 接口的處理類的 invoke()方法,可以在 invoke() 方法中自定義處理邏輯,比如在方法執行前后做什么事情。

代碼示例

1)定義一個接口(Subject)

  1. public interface SmsService { 
  2.     String send(String message); 

2)創建一個委托類(Real Subject)實現這個接口

  1. public class SmsServiceImpl implements SmsService { 
  2.     public String send(String message) { 
  3.         System.out.println("send message:" + message); 
  4.         return message; 
  5.     } 

3)創建一個處理類并實現 InvocationHandler 接口,重寫其 invoke 方法(在invoke 方法中利用反射機制調用委托類的方法,并自定義一些處理邏輯),并將委托類注入處理類

  1. import java.lang.reflect.InvocationHandler; 
  2. import java.lang.reflect.InvocationTargetException; 
  3. import java.lang.reflect.Method; 
  4.  
  5. public class DebugInvocationHandler implements InvocationHandler { 
  6.      
  7.     // 將委托類注入處理類(這里我們用 Object 代替,方便擴展) 
  8.     private final Object target; 
  9.  
  10.     public DebugInvocationHandler(Object target) { 
  11.         this.target = target; 
  12.     } 
  13.   
  14.     // 重寫 invoke 方法 
  15.     @Override 
  16.     public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { 
  17.         //調用方法之前,我們可以添加自己的操作 
  18.         System.out.println("before method " + method.getName()); 
  19.         Object result = method.invoke(target, args); 
  20.         //調用方法之后,我們同樣可以添加自己的操作 
  21.         System.out.println("after method " + method.getName()); 
  22.         return result; 
  23.     } 

4)定義一個創建代理對象(Proxy)的工廠類:通過 Proxy.newProxyInstance()創建委托類對象的代理對象

  1. public class JdkProxyFactory { 
  2.     public static Object getProxy(Object target) { 
  3.         return Proxy.newProxyInstance( 
  4.                 target.getClass().getClassLoader(), 
  5.                 target.getClass().getInterfaces(), 
  6.                 new DebugInvocationHandler(target) 
  7.         ); 
  8.     } 

5)實際使用

  1. SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl()); 
  2. smsService.send("Java"); 

運行上述代碼之后,控制臺打印出:

  1. before method send 
  2. send message:Java 
  3. after method send 

 

7. CGLIB 動態代理機制

使用步驟

「JDK 動態代理有一個最致命的問題是它只能代理實現了某個接口的實現類,并且代理類也只能代理接口中實現的方法,要是實現類中有自己私有的方法,而接口中沒有的話,該方法不能進行代理調用」。

為了解決這個問題,我們可以用 CGLIB 動態代理機制。

上文也提到過,CGLIB(Code Generation Library)是一個基于 ASM 的 Java 字節碼生成框架,它允許我們在運行時對字節碼進行修改和動態生成。原理就是「通過字節碼技術生成一個子類,并在子類中攔截父類方法的調用,織入額外的業務邏輯」。關鍵詞大家注意到沒有,攔截!CGLIB 引入一個新的角色就是「方法攔截器」MethodInterceptor。和 JDK 中的處理類 InvocationHandler 差不多,也是用來實現方法的統一調用的。看下圖:

 

另外由于 CGLIB 采用「繼承」的方式,所以被代理的類不能被 final 修飾。

很多知名的開源框架都使用到了 CGLIB, 例如 「Spring 中的 AOP 模塊中:如果目標對象實現了接口,則默認采用 JDK 動態代理,否則采用 CGLIB 動態代理」。

來看 CGLIB 動態代理的使用步驟:

1)首先創建一個委托類(Real Subject)

2)創建一個方法攔截器實現接口 MethodInterceptor,并重寫 intercept 方法。intercept 用于攔截并增強委托類的方法(和 JDK 動態代理 InvocationHandler中的 invoke 方法類似)

 

該方法擁有四個參數:

  • Object var1:委托類對象
  • Method var2:被攔截的方法(委托類中需要增強的方法)
  • Object[] var3:方法入參
  • MethodProxy var4:用于調用委托類的原始方法(底層也是通過反射機制,不過不是 Method.invoke 了,而是使用 MethodProxy.invokeSuper 方法)

 

3)創建代理對象(Proxy):通過 Enhancer.create() 創建委托類對象的代理對象

 

 

也就是說:我們在通過 Enhancer 類的 create() 創建的代理對象在調用方法的時候,實際會調用到實現了 MethodInterceptor 接口的處理類的 intercept()方法,可以在 intercept() 方法中自定義處理邏輯,比如在方法執行前后做什么事情。

❝可以發現,CGLIB 動態代理機制和 JDK 動態代理機制的步驟差不多,CGLIB 動態代理的核心是方法攔截器 MethodInterceptor 和 Enhancer,而 JDK 動態代理的核心是處理類 InvocationHandler 和 Proxy。❞

代碼示例

不同于 JDK 動態代理不需要額外的依賴。CGLIB 是一個開源項目,如果你要使用它的話,需要手動添加相關依賴。

  1. <dependency> 
  2.   <groupId>cglib</groupId> 
  3.   <artifactId>cglib</artifactId> 
  4.   <version>3.3.0</version> 
  5. </dependency> 

1)首先創建一個委托類(Real Subject)

  1. public class AliSmsService { 
  2.     public String send(String message) { 
  3.         System.out.println("send message:" + message); 
  4.         return message; 
  5.     } 

2)創建一個方法攔截器實現接口 MethodInterceptor,并重寫 intercept 方法

  1. import net.sf.cglib.proxy.MethodInterceptor; 
  2. import net.sf.cglib.proxy.MethodProxy; 
  3. import java.lang.reflect.Method; 
  4.  
  5. public class DebugMethodInterceptor implements MethodInterceptor { 
  6.  
  7.     @Override 
  8.     public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 
  9.         // 調用方法之前,我們可以添加自己的操作 
  10.         System.out.println("before method " + method.getName()); 
  11.         // 通過反射調用委托類的方法 
  12.         Object object = methodProxy.invokeSuper(o, args); 
  13.         // 調用方法之后,我們同樣可以添加自己的操作 
  14.         System.out.println("after method " + method.getName()); 
  15.         return object; 
  16.     } 
  17.  

3)創建代理對象(Proxy):通過 Enhancer.create() 創建委托類對象的代理對象

  1. import net.sf.cglib.proxy.Enhancer; 
  2.  
  3. public class CglibProxyFactory { 
  4.     public static Object getProxy(Class<?> clazz) { 
  5.         // 創建動態代理增強類 
  6.         Enhancer enhancer = new Enhancer(); 
  7.         // 設置類加載器 
  8.         enhancer.setClassLoader(clazz.getClassLoader()); 
  9.         // 設置委托類(設置父類) 
  10.         enhancer.setSuperclass(clazz); 
  11.         // 設置方法攔截器 
  12.         enhancer.setCallback(new DebugMethodInterceptor()); 
  13.         // 創建代理類 
  14.         return enhancer.create(); 
  15.     } 

❝從 setSuperclass 我們就能看出,為什么說 CGLIB 是基于繼承的。❞

4)實際使用

  1. AliSmsService aliSmsService =  
  2.     (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class); 
  3. aliSmsService.send("Java"); 

運行上述代碼之后,控制臺打印出:

  1. before method send 
  2. send message:Java 
  3. after method send 

JDK 動態代理和 CGLIB 動態代理對比

1)JDK 動態代理是基于實現了接口的委托類,通過接口實現代理;而 CGLIB 動態代理是基于繼承了委托類的子類,通過子類實現代理。

2)JDK 動態代理只能代理實現了接口的類,且只能增強接口中現有的方法;而 CGLIB 可以代理未實現任何接口的類。

3)就二者的效率來說,大部分情況都是 JDK 動態代理的效率更高,隨著 JDK 版本的升級,這個優勢更加明顯。

❝提一嘴,常見的還有 「Javassist 動態代理機制」。和 CGLIB 一樣,作為一個 Java 字節碼生成框架,Javassist 天生就擁有在運行時動態創建一個類的能力,實現動態代理自然不在話下。Dubbo 就是默認使用 Javassit 來進行動態代理的。❞

8. 什么情況下使用動態代理

1)設計模式中有一個設計原則是「開閉原則」,即「對修改關閉,對擴展開放」,我們在工作中有時會接手很多前人的代碼,里面代碼邏輯讓人摸不著頭腦,就很難去下手修改代碼,那么這時我們就可以通過代理對類進行增強。

2)我們在使用 「RPC 框架」的時候,框架本身并不能提前知道各個業務方要調用哪些接口的哪些方法 。那么這個時候,就可用通過動態代理的方式來建立一個中間人給客戶端使用,也方便框架進行搭建邏輯,某種程度上也是客戶端代碼和框架松耦合的一種表現。

3)「Spring 的 AOP」 機制同樣也是采用了動態代理,此處不做詳細討論。

9. 靜態代理和動態代理對比

1)「靈活性」 :動態代理更加靈活,不需要必須實現接口,可以直接代理實現類,并且可以不需要針對每個目標類都創建一個代理類。另外,靜態代理中,接口一旦新增加方法,目標對象和代理對象都要進行修改,這是非常麻煩的

2)「JVM 層面」 :靜態代理在編譯時就將接口、實現類、代理類這些都變成了一個個實際的 .class 字節碼文件。而動態代理是在運行時動態生成類字節碼,并加載到 JVM 中的。

10. 總結

全部捋一遍下來還是收獲蠻多的,我感覺只要理解了字節碼在編譯期生成還是在運行期生成,就差不多能夠把握住靜態代理和動態代理了。總結一下靜態代理和動態代理中的角色:

靜態代理:

  • Subject:公共接口
  • Real Subject:委托類
  • Proxy:代理類

JDK 動態代理:

  • Subject:公共接口
  • Real Subject:委托類
  • Proxy:代理類
  • 「InvocationHandler」:處理類,統一調用方法

CGLIB 動態代理:

  • Subject:公共接口
  • Real Subject:委托類
  • Proxy:代理類
  • 「MethodInterceptor」:方法攔截器,統一調用方法

參考資料《Java 核心技術 - 卷 1 基礎知識 - 第 10 版》

《Thinking In Java(Java 編程思想)- 第 4 版》

JavaGuide:https://snailclimb.gitee.io/javaguide

 

亦山 — Java動態代理機制詳解(JDK 和CGLIB,Javassist,ASM):https://blog.csdn.net/luanlouis/article/details/24589193

 

責任編輯:武曉燕 來源: 飛天小牛肉
相關推薦

2021-03-11 07:14:01

Epoll原理線程

2020-12-18 06:09:07

Java淺拷貝深拷貝

2021-08-12 10:36:18

order byMySQL數據庫

2017-12-26 14:17:24

潤乾報表

2023-01-10 19:47:47

Redis原理多線程

2021-06-15 07:15:15

Oracle底層explain

2022-01-17 20:59:37

開發group by思路

2024-05-21 08:40:21

分庫分表源碼

2025-02-13 09:06:27

2021-12-01 07:26:13

IO模型異步

2015-10-10 11:10:24

重敲代碼拷貝粘貼

2023-09-12 07:31:45

HashMap線程

2021-10-07 20:12:03

MVCC事務原理

2024-03-26 07:59:32

IO模型多路復用

2024-03-12 08:20:57

零拷貝存儲開發

2019-09-19 08:04:40

網絡七層模型TCPUDP

2020-02-09 17:30:54

反轉鏈表程序員節點

2022-02-22 09:16:41

AndroidWindows狀態欄

2010-01-02 10:38:51

360商業拓展dell

2022-08-26 10:41:03

指針C語言
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: www.日本三级 | 在线看h| 久久久久1| 欧美成人免费在线视频 | 一区二区福利视频 | 国产一区二区在线视频 | 欧美国产激情 | 欧美日韩精品一区二区三区视频 | 亚洲免费在线视频 | 亚洲乱码一区二区 | 国产剧情一区二区三区 | 亚洲精品久久久久久首妖 | 国产成人精品一区二区三区在线 | 亚洲精品电影网在线观看 | 岛国在线免费观看 | 国产一区不卡 | 亚洲欧洲在线视频 | 91成人在线| 性色综合 | 精品成人一区 | 99精品久久久国产一区二区三 | 99久久免费精品视频 | 日本成人中文字幕 | 成人免费一级视频 | 亚洲 91| 日日噜噜噜夜夜爽爽狠狠视频97 | 91偷拍精品一区二区三区 | 9191成人精品久久 | 午夜激情小视频 | 手机在线不卡av | 国产精品免费大片 | 久久性av| 国产激情视频在线免费观看 | 免费播放一级片 | 一级黄色淫片 | 欧美一区二区免费 | 久久69精品久久久久久久电影好 | 亚洲精品乱码久久久久久按摩观 | 最新中文字幕在线 | 中日韩毛片 | 欧美日韩亚洲一区二区 |