国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

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

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - JDK1.6“新“特性Instrumentation之JavaAgent(推薦)

JDK1.6“新“特性Instrumentation之JavaAgent(推薦)

2020-08-04 00:43大火yzs Java教程

這篇文章主要介紹了JDK1.6“新“特性Instrumentation之JavaAgent,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

簡介

Java Agent是在JDK1.5以后,我們可以使用agent技術構建一個獨立于應用程序的代理程序(即為Agent),用來協助監測、運行甚至替換其他JVM上的程序。使用它可以實現虛擬機級別的AOP功能。

Agent分為兩種,一種是在主程序之前運行的Agent,一種是在主程序之后運行的Agent(前者的升級版,1.6以后提供)。

JavaAgent的作用Agent給我們程序帶來的影響.jpg

JDK1.6“新“特性Instrumentation之JavaAgent(推薦)

使用Agent-premain方法影響的程序效果圖.jpg

JDK1.6“新“特性Instrumentation之JavaAgent(推薦)

使用Agent-agentmain方法影響的程序效果圖.jpg

JDK1.6“新“特性Instrumentation之JavaAgent(推薦)

JavaAgent相關的API

在java.lang.instrument包下 給我們提供了相關的API

而最為主要的就是Instrumentation這個接口中的幾個方法

JDK1.6“新“特性Instrumentation之JavaAgent(推薦)

?
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
public interface Instrumentation {
 
 /**
  * 添加Transformer(轉換器)
  * ClassFileTransformer類是一個接口,通常用戶只需實現這個接口的 byte[] transform()方法即可;
  * transform這個方法會返回一個已經轉換過的對象的byte[]數組
  * @param transformer   攔截器
  * @return canRetransform  是否能重新轉換
  */
    void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
 
 /**
  * 重新觸發類加載,
  * 該方法可以修改方法體、常量池和屬性值,但不能新增、刪除、重命名屬性或方法,也不能修改方法的簽名
  * @param classes   Class對象
  * @throws UnmodifiableClassException  異常
  */
 void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
 
 /**
  * 直接替換類的定義
  * 重新轉換某個對象,并已一個新的class格式,進行轉化。
  * 該方法可以修改方法體、常量池和屬性值,但不能新增、刪除、重命名屬性或方法,也不能修改方法的簽名
  * @param definitions   ClassDefinition對象[Class定義對象]
  * @throws ClassNotFoundException,UnmodifiableClassException  異常
  */
 void redefineClasses(ClassDefinition... definitions)throws ClassNotFoundException, UnmodifiableClassException;
 
 /**
  * 獲取當前被JVM加載的所有類對象
  * @return Class[]  class數組
  */
 Class[] getAllLoadedClasses();
}

后面我們會在代碼中具體用到這些方法。再詳細說明。

JavaAgent-premain方法1-初探效果:

實現main方法前執行業務邏輯

Agent1.java

?
1
2
3
4
5
public class Agent1 {
 public static void premain(String agent){
  System.out.println("Agent1 premain :" + agent);
 }
}

Demo1.java

?
1
2
3
4
5
6
7
8
9
10
public class Demo1 {
 
 /**
  * VM參數
  * -javaagent:D:\desktop\text\code\mycode\JavaAgentDemo\agent\target/agent.jar=input
  * */
 public static void main(String[] args) throws Exception {
  System.out.println("demo1");
 }
}

resources/META-INF/MANIFEST.MF

?
1
2
3
4
5
6
7
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: dahuoyzs
Created-By: Apache Maven 3.6.0
Build-Jdk: 1.8.0_171
Premain-Class: cn.bigfire.Agent1
Can-Retransform-Classes: true

運行效果

Agent1 premain :input
demo1

JavaAgent-premain方法2-實現修改代碼邏輯效果:

實現 修改 程序源代碼 hello -> hello agented

Agent2.java

?
1
2
3
4
5
6
7
8
9
10
11
12
public class Agent2 {
 /**
  * 可以運行在main方法啟動前
  * @param agent    輸入的參數
  * @param instrumentation    輸入的參數
  */
 public static void premain(String agent, Instrumentation instrumentation){
  System.out.println("Agent2 premain 2param :" + agent);
  instrumentation.addTransformer(new ConsoleTransformer(),true);
 }
 
}

ConsoleTransformer.java

?
1
2
3
4
5
6
7
8
9
10
11
public class ConsoleTransformer implements ClassFileTransformer {
 @Override
 public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
  if (className.equals("cn/bigfire/Console")){
   String root = StrUtil.subBefore(System.getProperty("user.dir"), "JavaAgentDemo", true);
   String classFile = root + "JavaAgentDemo/agent/src/main/resources/Console.class";
   return FileUtil.readBytes(classFile);
  }
  return classfileBuffer;
 }
}

Demo2.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Demo2 {
 
 /**
  * VM參數
  * -javaagent:D:\desktop\text\code\mycode\JavaAgentDemo\agent\target/agent.jar=input
  * */
 public static void main(String[] args) throws Exception {
  new Thread(()->{
   while (true){
    Console.hello();// public static void hello(){System.out.println("hello"); }
    ThreadUtil.sleep(2000);
   }
  }).start();
 }
}

resources/META-INF/MANIFEST.MF

?
1
2
3
4
5
6
7
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: dahuoyzs
Created-By: Apache Maven 3.6.0
Build-Jdk: 1.8.0_171
Premain-Class: cn.bigfire.Agent2
Can-Retransform-Classes: true

運行效果

Agent2 premain 2param :input
滿足條件
hello  agented
hello  agented
hello  agented
hello  agented

JavaAgent-premain方法3-無侵入動態修改程序源代碼實現方法耗時統計效果:

實現main方法外的所有方法統計時間

Agent3.java

?
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
59
60
61
62
63
64
public class Agent3 {
 /**
  * 可以運行在main方法啟動前
  * @param agent       輸入的參數
  * @param instrumentation    instrumentation對象由JVM提供并傳入
  */
 public static void premain(String agent, Instrumentation instrumentation) {
  System.out.println("Agent3 premain :" + agent);
  instrumentation.addTransformer(new TimeCountTransformer());
 }
 
 /**
  * 時間統計Transformer 給要代理的方法添加時間統計
  */
 private static class TimeCountTransformer implements ClassFileTransformer {
  @Override
  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
   try {
    className = className.replace("/", ".");
    if (className.equals("cn.bigfire.Demo3")) {
     //使用全稱,用于取得字節碼類<使用javassist>
     CtClass ctclass = ClassPool.getDefault().get(className);
     //獲得方法列表
     CtMethod[] methods = ctclass.getDeclaredMethods();
     //給方法設置代理
     Stream.of(methods).forEach(method-> agentMethod(ctclass,method));
     //CtClass轉byte[]數組
     return ctclass.toBytecode();
    }
   } catch (Exception e) {
    e.printStackTrace();
   }
   return null;
  }
 }
 
 /**
  * 代理方法,把傳入的方法經寫代理,并生成帶時間統計的方法,
  * @param ctClass       javassist的Class類
  * @param ctMethod      javassist的ctMethod方法
  * */
 public static void agentMethod(CtClass ctClass,CtMethod ctMethod){
  try {
   String mName = ctMethod.getName();
   if (!mName.equals("main")){//代理除了main方法以外的所有方法
    String newName = mName + "$Agent";
    ctMethod.setName(newName);
    CtMethod newMethod = CtNewMethod.copy(ctMethod, mName, ctClass, null);
    // 構建新的方法體
    String bodyStr = "{\n" +
      "long startTime = System.currentTimeMillis();\n" +
      newName + "();\n" +
      "long endTime = System.currentTimeMillis();\n" +
      "System.out.println(\""+newName+"() cost:\" +(endTime - startTime));\n" +
      "}";
    newMethod.setBody(bodyStr);// 替換新方法
    ctClass.addMethod(newMethod);// 增加新方法
   }
  }catch (Exception e){
   e.printStackTrace();
  }
 }
 
}

Demo3.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Demo3 {
 
 /**
  * VM參數
  * -javaagent:D:\desktop\text\code\mycode\JavaAgentDemo\agent\target/agent.jar=input
  */
 public static void main(String[] args) throws Exception {
  sleep1();
  sleep2();
 }
 
 public static void sleep1(){
  ThreadUtil.sleep(1000);
 }
 
 public static void sleep2(){
  ThreadUtil.sleep(2000);
 }
 
}

resources/META-INF/MANIFEST.MF

?
1
2
3
4
5
6
7
8
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: dahuoyzs
Created-By: Apache Maven 3.6.0
Build-Jdk: 1.8.0_171
Class-Path: ../javassist-3.12.1.GA.jar
Premain-Class: cn.bigfire.Agent3
Can-Retransform-Classes: true

運行效果

Agent3 premain :input
sleep1$Agent() cost:1005
sleep2$Agent() cost:2001

JavaAgent-agentmain方法1-實現運行時修改程序效果:

實現運行時 修改程序 hello -> hello agented

Agent4.java

?
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
public class Agent4 {
 
 public static void premain(String agent){
  System.out.println("Agent4 premain 1param:" + agent);
 }
 
 public static void premain(String agent, Instrumentation instrumentation) {
  System.out.println("Agent4 premain 2param:" + agent);
  //premain時,由于堆里還沒有相應的Class。所以直接addTransformer,程序就會生效。
//  instrumentation.addTransformer(new ConsoleTransformer(),true);
 }
 
 public static void agentmain(String agent, Instrumentation instrumentation){
  System.out.println("Agent4 agentmain 2param :" + agent);
  instrumentation.addTransformer(new ConsoleTransformer(),true);
  //agentmain運行時 由于堆里已經存在Class文件,所以新添加Transformer后
  // 還要再調用一個 inst.retransformClasses(clazz); 方法來更新Class文件
  for (Class clazz:instrumentation.getAllLoadedClasses()) {
   if (clazz.getName().contains("cn.bigfire.Console")){
    try {
     instrumentation.retransformClasses(clazz);
    } catch (Exception e) {
     e.printStackTrace();
    }
   }
  }
 }
 
 public static void agentmain(String agent){
  System.out.println("Agent4 agentmain 1param :" + agent);
 }
 
}

Demo4

?
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
public class Demo4 {
 /**
  * 打包agent4 -> 先運行demo2 -> 運行demo4 ->選擇程序demo2結尾的程序,即可運行時修改文件
  * VM參數
  * -javaagent:D:\desktop\text\code\mycode\JavaAgentDemo\agent\target/agent.jar=input
  * */
 public static void main(String[] args) throws Exception {
  while (true){
   List<VirtualMachineDescriptor> list = VirtualMachine.list();
   for (int i = 0; i < list.size(); i++) {
    VirtualMachineDescriptor jvm = list.get(i);;
    System.out.println("[" +i+ "]ID:"+jvm.id()+",Name:"+jvm.displayName());
   }
   System.out.println("請選擇第幾個");
   Scanner scanner = new Scanner(System.in);
   int s = scanner.nextInt();
   VirtualMachineDescriptor virtualMachineDescriptor = list.get(s);
   VirtualMachine attach = VirtualMachine.attach(virtualMachineDescriptor.id());
   String root = StrUtil.subBefore(System.getProperty("user.dir"), "JavaAgentDemo", true);
   String agentJar = root + "JavaAgentDemo\\agent\\target\\agent.jar";
   File file = new File(agentJar);
   System.out.println(file.exists());
   attach.loadAgent(agentJar,"param");
   attach.detach();
  }
 }
}

resources/META-INF/MANIFEST.MF

?
1
2
3
4
5
6
7
8
9
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: dahuoyzs
Created-By: Apache Maven 3.6.0
Build-Jdk: 1.8.0_171
Premain-Class: cn.bigfire.Agent4
Agent-Class: cn.bigfire.Agent4
Can-Retransform-Classes: true
Can-Redefine-Classes: true

此時的運行順序
打包agent4 -> 先運行demo2 -> 運行demo4 ->選擇程序demo2結尾的程序,即可運行時修改文件

運行效果
Demo2

Agent4 premain 2param:input
hello
hello

Demo4

?
1
2
3
4
5
6
7
8
9
[0]ID:12480,Name:cn.bigfire.Demo2
[1]ID:14832,Name:org.jetbrains.kotlin.daemon.KotlinCompileDaemon --daemon-runFilesPath xxx
[2]ID:14864,Name:
[3]ID:3952,Name:cn.bigfire.Demo4
[4]ID:14852,Name:org.jetbrains.idea.maven.server.RemoteMavenServer36
[5]ID:11928,Name:org.jetbrains.jps.cmdline.Launcher xxx
請選擇第幾個
0
true

Demo2

?
1
2
3
4
5
6
7
Agent4 premain 2param:input
hello
hello
Agent4 agentmain 2param :param
hello agented
hello agented
hello agented

JavaAgent-agentmain方法2-實現動態修改日志級別效果:

實現運行時 修改程序 模擬項目中的動態日志 info <-> debug

Agent5.java

?
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public class Agent5 {
 
 public static void premain(String agent, Instrumentation instrumentation){
  System.out.println("Agent5 premain 2param :" + agent);
  instrumentation.addTransformer(new StartTransformer(),true);
 
  //這個方式不行。因為啟動時Class都還沒有呢。
//  for (Class clazz:inst.getAllLoadedClasses()) {
//   if (clazz.getName().equals("cn.bigfire.LogLevelStarter")){
//    try {
//     switchDebug(clazz);
//     instrumentation.retransformClasses(clazz);
//    } catch (Exception e) {
//     e.printStackTrace();
//    }
//   }
//  }
 }
 
 public static void agentmain(String agent, Instrumentation instrumentation){
  System.out.println("Agent5 agentmain 2param :" + agent);
  for (Class clazz:instrumentation.getAllLoadedClasses()) {
   if (clazz.getName().equals("cn.bigfire.LogLevelStarter")){
    try {
     switchAtomicDebug(clazz);
     instrumentation.retransformClasses(clazz);
    } catch (Exception e) {
     e.printStackTrace();
    }
   }
  }
 }
 
 public static class StartTransformer implements ClassFileTransformer {
  @Override
  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
   //此時由于classBeingRedefined是空,所以還是不能用這個Class修改屬性呢,只能通過 讀取byte[]往堆里丟,才能用。
   if (className.equals("cn/bigfire/LogLevelStarter")){
    //【這是一個錯誤的思路】 premain的時候 classBeingRedefined是空的因為很多的Class還沒加載到堆中
//    if (classBeingRedefined!=null){
//     switchDebug(classBeingRedefined);
//     return toBytes(classBeingRedefined);
//    }
    //正常的讀取一共文件byte[]數組
    String root = StrUtil.subBefore(System.getProperty("user.dir"), "JavaAgentDemo", true);
    String classFile = root + "JavaAgentDemo/agent/src/main/resources/LogLevelStarter.class";
    return FileUtil.readBytes(classFile);
   }
   return classfileBuffer;
  }
 }
 
 /**
  * 可序列化對象轉byte[]數組
  * @param clazz    要轉byte[]數組的對象
  * @return byte[]   返回byte[]數組
  */
 public static byte[] toBytes(Serializable clazz){
  try {
   ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
   ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
   stream.writeObject(clazz);
   return byteArrayOutputStream.toByteArray();
  }catch (Exception e){
   e.printStackTrace();
  }
  return null;
 }
 
 public static void switchDebug(Class clazz){
  try {
   Field field1 = clazz.getDeclaredField("isDebug");
   field1.setAccessible(true);
   boolean debug = field1.getBoolean(clazz);
   field1.setBoolean(clazz,!debug);
  }catch (Exception e){
   e.printStackTrace();
  }
 }
 
 public static void switchAtomicDebug(Class clazz){
  try {
   Field field2 = clazz.getDeclaredField("atomicDebug");
   field2.setAccessible(true);
   AtomicBoolean atomicDebug = (AtomicBoolean)field2.get(clazz);
   atomicDebug.set(!atomicDebug.get());
  }catch (Exception e){
   e.printStackTrace();
  }
 }
 
}

注意,需要先把LogLevelStarter.java中的isDebug 改為true編譯一下。放到src/main/resources/目錄下;

LogLevelStarter.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LogLevelStarter {
 
 public static volatile boolean isDebug = false;
 public static AtomicBoolean atomicDebug = new AtomicBoolean(false);
 
 /**
  * VM參數
  * -javaagent:D:\desktop\text\code\mycode\JavaAgentDemo\agent\target/agent.jar=input
  */
 public static void main(String[] args) throws Exception {
  new Thread(()->{
   for (;;){
    //死循環,每隔兩秒打印一個日志。
    System.out.print(isDebug ? "volatile debug" : "volatile info");
    System.out.print("\t");
    System.out.println(atomicDebug.get() ? "atomicDebug debug" : "atomicDebug info");
    ThreadUtil.sleep(2000);
   }
  }).start();
 }
}

resources/META-INF/MANIFEST.MF

?
1
2
3
4
5
6
7
8
9
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: dahuoyzs
Created-By: Apache Maven 3.6.0
Build-Jdk: 1.8.0_171
Premain-Class: cn.bigfire.Agent5
Agent-Class: cn.bigfire.Agent5
Can-Retransform-Classes: true
Can-Redefine-Classes: true

此時的運行順序
打包agent5 -> 先運行LogLevelStarter -> 運行demo4 ->選擇程序LogLevelStarter結尾的程序,即可運行時修改文件

運行效果

LogLevelStarter

Agent5 premain 2param :input
volatile debug atomicDebug info
volatile debug atomicDebug info

Demo4

?
1
2
3
4
5
6
7
8
9
[0]ID:12592,Name:cn.bigfire.LogLevelStarter
[1]ID:12880,Name:cn.bigfire.Demo4
[2]ID:14832,Name:org.jetbrains.kotlin.daemon.KotlinCompileDaemon --daemon-runFilesPath xxx
[3]ID:14864,Name:
[4]ID:14852,Name:org.jetbrains.idea.maven.server.RemoteMavenServer36
[5]ID:8116,Name:org.jetbrains.jps.cmdline.Launcher xxx
請選擇第幾個
0
true

LogLevelStarter

?
1
2
3
4
5
6
Agent5 premain 2param :input
volatile debug  atomicDebug info
volatile debug  atomicDebug info
Agent5 agentmain 2param :param
volatile debug  atomicDebug debug
volatile debug  atomicDebug debug

在Agent5中,其實使用premain和agentmain。

premain把volatile修飾的isDbug給修改為true了。

而agentmain時把atomicDebug的值進行多次取反操作。

自己實現一個熱部署功能的大致思路

當運行完本項目中的幾個demo之后。

讀者可能對Java Agent有了一些基本的概念

最起碼我們知道了premain是可以運行在main函數前的。

agentmain是可以在程序運行時,修改程序內的一些類文件的。

那么熱部署很明顯就是使用的agentmain這個特性了

那么熱部署具體應該怎么實現呢?

這里先有個大概的思路。后續如果有經歷,可以簡單按照這個思路實現一下

思路

當我們文件發生修改的時候,項目會重新加載我們的類。

那么這里肯定會涉及到文件變化的觀察。 即 觀察者設計模式跑不了

首先遞歸當前項目目錄。并根據文件類型,如(.java ,xml,yml等)將此類文件注冊觀察者模式。

當文件內容發生變化時,會調用 監聽器中的回調方法;

在回調中完成如下(具體實現時未必需要)

使用Java1.6的JavaCompiler編譯Java文件;
自定義ClassLoader 裝載 編譯好的Class到堆中

使用agentmain修改原Class文件替換成新的Class文件

完成熱加載

JavaAgent的應用場景

apm:(Application Performance Management)應用性能管理。pinpoint、cat、skywalking等都基于Instrumentation實現
idea的HotSwap、Jrebel等熱部署工具
應用級故障演練
Java診斷工具Arthas、Btrace等

源代碼

?
1
2
3
4
5
6
{
    "author": "大火yzs",
    "title": "【JavaAgent】JavaAgent入門教程",
    "tag": "JavaAgent,Instrumentation,運行時動態修改源程序",
    "createTime": "2020-08-02 18:30"
}

總結

到此這篇關于JDK1.6“新“特性Instrumentation之JavaAgent的文章就介紹到這了,更多相關JDK1.6“新“特性Instrumentation內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://blog.csdn.net/qq_34173920/article/details/107748852

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 热久久影院 | 在线观看免费成人av | 国产精品激情 | а天堂中文最新一区二区三区 | 欧美一级片在线 | 久久久久国产一区二区三区四区 | 精品日韩在线 | 日韩在线免费观看网站 | 亚洲欧美日韩另类精品一区二区三区 | 欧美黄色性视频 | 欧美日韩一区二区三区不卡视频 | 欧美在线观看免费观看视频 | 国产亚洲一区二区精品 | 国产免费视频在线 | 欧美一级黄色片免费看 | 亚洲天堂久久 | 久久作爱视频 | 国产精品99久久久久久久vr | 日韩av中文| 国产黄色电影 | 亚洲黄页 | 99精品欧美一区二区三区综合在线 | 欧美黄页| 亚洲免费视频一区 | 九九热免费精品视频 | 韩国一区二区视频 | 国产日韩一区二区三区 | 成人爽a毛片一区二区免费 日韩av高清在线 | 亚洲国产一区二区在线观看 | 成人av入口 | 亚洲精品一区 | 五月婷婷综合网 | 久久久久久免费精品 | 中文字幕不卡 | 国内精品在线视频 | 日一区二区| 欧洲成人午夜免费大片 | 黄色a级 | 久久久久久成人 | 久久久一| 伊人狠狠 |