日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Javassist用法詳解

瀏覽:36日期:2022-08-17 08:14:53
概述

Java字節碼以二進制的形式存儲在.class文件中,每一個.class文件包含一個Java類或接口。Javaassist就是一個用來處理Java字節碼的類庫。它可以在一個已經編譯好的類中添加新的方法,或者是修改已有的方法,并且不需要對字節碼方面有深入的了解。同時也可以通過完全手動的方式生成一個新的類對象。

Maven依賴方式:

<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.27.0-GA</version></dependency>

Gradle依賴方式:

implementation ’org.javassist:javassist:3.27.0-GA’ClassPool

ClassPool是CtClass對象的容器,它按需讀取類文件來構造CtClass對象,并且保存CtClass對象以便以后使用。

從實現的角度來看,ClassPool 是一個存儲 CtClass 的 Hash 表,類的名稱作為 Hash 表的 key。ClassPool 的 get() 函數用于從 Hash 表中查找 key 對應的 CtClass 對象。如果沒有找到,get() 函數會創建并返回一個新的 CtClass 對象,這個新對象會保存在 Hash 表中。

需要注意的是ClassPool會在內存中維護所有被它創建過的CtClass,當CtClass數量過多時,會占用大量的內存,API中給出的解決方案是重新創建ClassPool 或 有意識的調用CtClass的detach()方法以釋放內存。

ClassPool需要關注的方法:

getDefault:返回默認的ClassPool,一般通過該方法創建我們的ClassPool; appendClassPath, insertClassPath:將一個ClassPath加到類搜索路徑的末尾位置 或 插入到起始位置。通常通過該方法寫入額外的類搜索路徑,以解決多個類加載器環境中找不到類的尷尬; toClass:將修改后的CtClass加載至當前線程的上下文類加載器中,CtClass的toClass方法是通過調用本方法實現。需要注意的是一旦調用該方法,則無法繼續修改已經被加載的class; makeClass:根據類名創建新的CtClass對象; get,getCtClass:根據類路徑名獲取該類的CtClass對象,用于后續的編輯。

可以使用toBytecode()函數來獲取修改過的字節碼:

byte[] b = cc.toBytecode();

也可以通過toClass()函數直接將CtClass轉換成Class對象:

Class clazz = cc.toClass();

toClass()會請求當前線程的ClassLoader加載CtClass所代表的類文件,它返回此類文件的java.lang.Class對象。

CtClass

CtClass類表示一個class文件,每個CtClass對象都必須從ClassPool中獲取,CtClass需要關注的方法:

freeze:凍結一個類,使其不可修改; isFrozen:判斷一個類是否已被凍結; defrost:解凍一個類,使其可以被修改; prune:刪除類不必要的屬性,以減少內存占用。調用該方法后,許多方法無法將無法正常使用,慎用; detach:將該class從ClassPool中刪除; writeFile:根據CtClass生成.class文件; toClass:通過類加載器加載該CtClass; addField,removeField:添加/移除一個CtField; addMethod,removeMethod:添加/移除一個CtMethod; addConstructor,removeConstructor:添加/移除一個CtConstructor。

如果一個 CtClass 對象通過 writeFile(), toClass(), toBytecode() 被轉換成一個類文件,此 CtClass 對象會被凍結起來,不允許再修改,因為一個類只能被 JVM 加載一次。

但是,一個冷凍的 CtClass 也可以被解凍,例如:

CtClasss cc = ...;cc.writeFile();cc.defrost();cc.setSuperclass(...); // 因為類已經被解凍,所以這里可以調用成功

調用 defrost() 之后,此 CtClass 對象又可以被修改了。

如果 ClassPool.doPruning 被設置為 true,Javassist 在凍結 CtClass 時,會修剪 CtClass 的數據結構。為了減少內存的消耗,修剪操作會丟棄 CtClass 對象中不必要的屬性。例如,Code_attribute 結構會被丟棄。一個 CtClass 對象被修改之后,方法的字節碼是不可訪問的,但是方法名稱、方法簽名、注解信息可以被訪問。修剪過的 CtClass 對象不能再次被解凍。ClassPool.doPruning 的默認值為 false。

stopPruning() 可以用來駁回修剪操作。

CtClasss cc = ...;cc.stopPruning(true);cc.writeFile(); // 轉換成一個 class 文件// cc is not pruned.

這個 CtClass 沒有被修剪,所以在 writeFile() 之后,可以被解凍。

注意:調試的時候可能臨時需要停止修剪和凍結,然后保存一個修改過的類文件到磁盤,debugWriteFile() 方法正是為此準備的。它停止修剪,然后寫類文件,然后解凍并再次打開修剪(如果開始時修養是打開的)。

CtMthod

CtMthod代表類中的某個方法,可以通過CtClass提供的API獲取 或者 構造方法 或者 CtNewMethod.make()方法新建,通過CtMethod對象可以實現對方法的修改。

CtMethod中的一些重要方法:

insertBefore:在方法的起始位置插入代碼; insterAfter:在方法的所有 return 語句前插入代碼以確保語句能夠被執行,除非遇到exception; insertAt:在指定的位置插入代碼; setBody:將方法的內容設置為要寫入的代碼,當方法被abstract修飾時,該修飾符被移除; make:創建一個新的方法。

CtNewMethod是一個用來創建CtMethod實例的類,其一個make方法如下:

public static CtMethod make(int modifiers, CtClass returnType, String mname, CtClass[] parameters, CtClass[] exceptions, String body, CtClass declaring) throws CannotCompileException{ try { CtMethod cm = new CtMethod(returnType, mname, parameters, declaring); cm.setModifiers(modifiers); cm.setExceptionTypes(exceptions); cm.setBody(body); return cm; } catch (NotFoundException e) { throw new CannotCompileException(e); }}

也可以通過CtNewMethod.setter/getter方法為某個屬性創建get/set方法:

CtClass cc = ...;CtField param = ...;cc.addMethod(CtNewMethod.setter('setName', param));cc.addMethod(CtNewMethod.getter('getName', param));CtField

CtField代表類中的某個屬性,可以直接通過其構造方法創建實例:

CtClass cc = pool.makeClass('com.hearing.demo.Person');CtField param = new CtField(pool.get('java.lang.String'), 'name', cc);param.setModifiers(Modifier.PRIVATE);cc.addField(param, CtField.Initializer.constant('hearing'));CtConstructor

CtConstructor代表類中的一個構造器,可以通過CtConstructor.make方法創建:

public static CtConstructor make(CtClass[] parameters, CtClass[] exceptions, String body, CtClass declaring) throws CannotCompileException{ try { CtConstructor cc = new CtConstructor(parameters, declaring); cc.setExceptionTypes(exceptions); cc.setBody(body); return cc; } catch (NotFoundException e) { throw new CannotCompileException(e); }}

也可以通過構造方法直接創建:

CtConstructor cons = new CtConstructor(new CtClass[]{pool.get('java.lang.String')}, cc);// $0=this / $1,$2,$3... 代表方法參數cons.setBody('{$0.name = $1;}');cc.addConstructor(cons);ClassPath

ClassPath是一個接口,代表類的搜索路徑,含有具體的搜索實現。當通過其它途徑無法獲取要編輯的類時,可以嘗試定制一個自己的ClassPath。API提供的實現中值得關注的有:

ByteArrayClassPath:將類以字節碼的形式加入到該path中,ClassPool可以從該path中生成所需的CtClass。 ClassClassPath:通過某個class生成的path,通過該class的classloader來嘗試加載指定的類文件。 LoaderClassPath:通過某個classloader生成path,并通過該classloader搜索加載指定的類文件。需要注意的是該類加載器以弱引用的方式存在于path中,當不存在強引用時,隨時可能會被清理。

通過 ClassPool.getDefault() 獲取的 ClassPool 使用 JVM 的類搜索路徑。如果程序運行在 JBoss 或者 Tomcat 等 Web 服務器上,ClassPool 可能無法找到用戶的類,因為 Web 服務器使用多個類加載器作為系統類加載器。在這種情況下,ClassPool 必須添加額外的類搜索路徑。

下面的例子中,pool 代表一個 ClassPool 對象:

pool.insertClassPath(new ClassClassPath(this.getClass()));

上面的語句將 this 指向的類添加到 pool 的類加載路徑中。你可以使用任意 Class 對象來代替 this.getClass(),從而將 Class 對象添加到類加載路徑中。

也可以注冊一個目錄作為類搜索路徑。下面的例子將 /usr/local/javalib 添加到類搜索路徑中:

ClassPool pool = ClassPool.getDefault();pool.insertClassPath('/usr/local/javalib');

類搜索路徑不但可以是目錄,還可以是 URL :

ClassPool pool = ClassPool.getDefault();ClassPath cp = new URLClassPath('www.javassist.org', 80, '/java/', 'org.javassist.');pool.insertClassPath(cp);

上述代碼將 http://www.javassist.org:80/java/ 添加到類搜索路徑。并且這個URL只能搜索 org.javassist 包里面的類。例如,為了加載 org.javassist.test.Main,它的類文件會從獲取 http://www.javassist.org:80/java/org/javassist/test/Main.class 獲取。

此外,也可以直接傳遞一個 byte 數組給 ClassPool 來構造一個 CtClass 對象,完成這項操作,需要使用 ByteArrayPath 類。示例:

ClassPool cp = ClassPool.getDefault();byte[] b = a byte array;String name = class name;cp.insertClassPath(new ByteArrayClassPath(name, b));CtClass cc = cp.get(name);

示例中的 CtClass 對象表示 b 代表的 class 文件。將對應的類名傳遞給 ClassPool 的 get() 方法,就可以從 ByteArrayClassPath 中讀取到對應的類文件。

如果你不知道類的全名,可以使用 makeClass() 方法:

ClassPool cp = ClassPool.getDefault();InputStream ins = an input stream for reading a class file;CtClass cc = cp.makeClass(ins);

makeClass() 返回從給定輸入流構造的 CtClass 對象。你可以使用 makeClass() 將類文件提供給 ClassPool 對象。如果搜索路徑包含大的 jar 文件,這可能會提高性能。由于 ClassPool 對象按需讀取類文件,它可能會重復搜索整個 jar 文件中的每個類文件。 makeClass() 可以用于優化此搜索。由 makeClass() 構造的 CtClass 保存在 ClassPool 對象中,從而使得類文件不會再被讀取。

用戶可以通過實現 ClassPath 接口來擴展類加載路徑,然后調用 ClassPool 的 insertClassPath() 方法將路徑添加進來。這種技術主要用于將非標準資源添加到類搜索路徑中。

ClassLoader

CtClass的toClass()方法請求當前線程的上下文類加載器,加載CtClass對象所表示的類:

public class Hello { public void say() { System.out.println('Hello'); }}public class Test { public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass cc = cp.get('Hello'); CtMethod m = cc.getDeclaredMethod('say'); m.insertBefore('{ System.out.println('Hello.say():'); }'); Class c = cc.toClass(); Hello h = (Hello) c.newInstance(); h.say(); }}

注意:上面的程序要正常運行,Hello 類在調用 toClass() 之前不能被加載。如果 JVM 在 toClass() 調用之前加載了原始的 Hello 類,后續加載修改的 Hello 類將會失敗(LinkageError 拋出)。例如,如果 Test 中的 main() 是這樣的:

public static void main(String[] args) throws Exception { Hello orig = new Hello(); ClassPool cp = ClassPool.getDefault(); CtClass cc = cp.get('Hello');}

那么,原始的 Hello 類在 main 的第一行被加載,toClass() 調用會拋出一個異常,因為類加載器不能同時加載兩個不同版本的 Hello 類。

如果程序在某些應用程序服務器(如JBoss和Tomcat)上運行,toClass()使用的上下文類加載器可能是不合適的。在這種情況下,你會看到一個意想不到的 ClassCastException。為了避免這個異常,必須給 toClass() 指定一個合適的類加載器。例如:

CtClass cc = ...;Class c = cc.toClass(bean.getClass().getClassLoader());

應該給toClass()傳遞加載了你的程序的類加載器(上例中,bean對象的類),toClass() 是為了簡便而提供的方法,如果你需要更復雜的功能,你應該編寫自己的類加載器。

Javassit 提供一個類加載器 javassist.Loader。它使用 javassist.ClassPool 對象來讀取類文件。

例如,javassist.Loader 可以用于加載用 Javassist 修改過的類。

public class Main { public static void main(String[] args) throws Throwable { ClassPool pool = ClassPool.getDefault(); Loader cl = new Loader(pool); CtClass ct = pool.get('test.Rectangle'); ct.setSuperclass(pool.get('test.Point')); Class c = cl.loadClass('test.Rectangle'); Object rect = c.newInstance(); }}

這個程序將 test.Rectangle 的超類設置為 test.Point。然后再加載修改的類,并創建新的 test.Rectangle 類的實例。

如果用戶希望在加載時按需修改類,則可以向 javassist.Loader 添加事件監聽器。當類加載器加載類時會通知監聽器。事件監聽器類必須實現以下接口:

public interface Translator { public void start(ClassPool pool) throws NotFoundException, CannotCompileException; public void onLoad(ClassPool pool, String classname) throws NotFoundException, CannotCompileException;}

當事件監聽器通過 addTranslator() 添加到 javassist.Loader 對象時,start() 方法會被調用。在 javassist.Loader 加載類之前,會調用 onLoad() 方法。可以在 onLoad() 方法中修改被加載的類的定義。

例如,下面的事件監聽器在類加載之前,將所有類更改為 public 類。

public class MyTranslator implements Translator { void start(ClassPool pool) throws NotFoundException, CannotCompileException {} void onLoad(ClassPool pool, String classname) throws NotFoundException, CannotCompileException { CtClass cc = pool.get(classname); cc.setModifiers(Modifier.PUBLIC); }}

注意,onLoad() 不必調用 toBytecode() 或 writeFile(),因為 javassist.Loader 會調用這些方法來獲取類文件。

示例創建Class文件

public class App { public static void main(String[] args) { try { createPerson(); } catch (Exception e) { e.printStackTrace(); } } private static void createPerson() throws Exception { ClassPool pool = ClassPool.getDefault(); // 1. 創建一個空類 CtClass cc = pool.makeClass('com.hearing.demo.Person'); // 2. 新增一個字段 private String name = 'hearing'; CtField param = new CtField(pool.get('java.lang.String'), 'name', cc); param.setModifiers(Modifier.PRIVATE); cc.addField(param, CtField.Initializer.constant('hearing')); // 3. 生成 getter、setter 方法 cc.addMethod(CtNewMethod.setter('setName', param)); cc.addMethod(CtNewMethod.getter('getName', param)); // 4. 添加無參的構造函數 CtConstructor cons = new CtConstructor(new CtClass[]{}, cc); cons.setBody('{name = 'hearing';}'); cc.addConstructor(cons); // 5. 添加有參的構造函數 cons = new CtConstructor(new CtClass[]{pool.get('java.lang.String')}, cc); // $0=this / $1,$2,$3... 代表方法參數 cons.setBody('{$0.name = $1;}'); cc.addConstructor(cons); // 6. 創建一個名為printName方法,無參數,無返回值,輸出name值 CtMethod ctMethod = new CtMethod(CtClass.voidType, 'printName', new CtClass[]{}, cc); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody('{System.out.println(name);}'); cc.addMethod(ctMethod); //這里會將這個創建的類對象編譯為.class文件 cc.writeFile('/.../path/'); }}

創建的class文件如下:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package com.hearing.demo;public class Person { private String name = 'hearing'; public void setName(String var1) { this.name = var1; } public String getName() { return this.name; } public Person() { this.name = 'hearing'; } public Person(String var1) { this.name = var1; } public void printName() { System.out.println(this.name); }}調用生成的類對象

1.通過反射的方式調用:

Object person = cc.toClass().newInstance();Method setName = person.getClass().getMethod('setName', String.class);setName.invoke(person, 'hearing1');Method execute = person.getClass().getMethod('printName');execute.invoke(person);

2.通過讀取class文件的方式調用:

ClassPool pool = ClassPool.getDefault();// 設置類路徑pool.appendClassPath('/.../path/');CtClass ctClass = pool.get('com.hearing.demo.Person');Object person = ctClass.toClass().newInstance();// 下面和通過反射的方式一樣去使用

3.通過接口的方式:

上面兩種其實都是通過反射的方式去調用,問題在于我們的工程中其實并沒有這個類對象,所以反射的方式比較麻煩,并且開銷也很大。那么如果你的類對象可以抽象為一些方法的合集,就可以考慮為該類生成一個接口類。這樣在newInstance()的時候我們就可以強轉為接口,可以將反射的那一套省略掉了。

還拿上面的Person類來說,新建一個IPerson接口類:

public interface IPerson { void setName(String name); String getName(); void printName();}

實現部分的代碼如下:

ClassPool pool = ClassPool.getDefault();pool.appendClassPath('/.../path/');// 獲取接口CtClass codeClassI = pool.get('com.hearing.demo.IPerson');// 獲取上面生成的類CtClass ctClass = pool.get('com.hearing.demo.Person');// 使代碼生成的類,實現 IPerson 接口ctClass.setInterfaces(new CtClass[]{codeClassI});// 以下通過接口直接調用 強轉IPerson person = (IPerson)ctClass.toClass().newInstance();System.out.println(person.getName());person.setName('hearing1');person.printName();修改現有的類對象

有如下類對象:

public class Test { public void test1() { System.out.println('I am test1'); }}

然后進行修改:

public class App { public static void main(String[] args) { try { update(); } catch (Exception e) { e.printStackTrace(); } } private static void update() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get('com.hearing.demo.Test'); CtMethod personFly = cc.getDeclaredMethod('test1'); personFly.insertBefore('System.out.println('...before...');'); personFly.insertAfter('System.out.println('...after...');'); //新增一個方法 CtMethod ctMethod = new CtMethod(CtClass.voidType, 'test2', new CtClass[]{}, cc); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody('{System.out.println('I am test2');}'); cc.addMethod(ctMethod); Object test = cc.toClass().newInstance(); Method personFlyMethod = test.getClass().getMethod('test1'); personFlyMethod.invoke(test); Method execute = test.getClass().getMethod('test2'); execute.invoke(test); }}

需要注意的是:上面的insertBefore() 和 setBody()中的語句,如果是單行語句可以直接用雙引號,但是有多行語句的情況下,需要將多行語句用{}括起來。javassist只接受單個語句或用大括號括起來的語句塊。

以上就是Javassist用法詳解的詳細內容,更多關于Javassist用法的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产黄色一区| 国产精品nxnn| 免费国产亚洲视频| 免费一区二区三区在线视频| 日本午夜精品一区二区三区电影 | 国产精品二区不卡| 99tv成人| 国产成人免费| 国产精品亚洲成在人线| 成人国产精品一区二区网站| 91亚洲精品视频在线观看| 婷婷激情图片久久| 蜜桃视频第一区免费观看| 91精品在线观看国产| 久久精品一区| 国产精选一区| 欧美日韩国产免费观看视频| 国产福利一区二区三区在线播放| 久久精品免视看国产成人| 激情综合自拍| 国产精品日本一区二区不卡视频 | 美女视频黄 久久| 婷婷成人av| 亚洲精品福利电影| 欧美日韩精品一本二本三本| 91精品国产自产在线丝袜啪| 国产精品亚洲综合色区韩国| 国产麻豆一区二区三区精品视频| 蜜桃久久久久久| 香蕉国产精品| 蜜桃伊人久久| 日韩一区精品视频| 日本va欧美va精品| 日韩免费精品| 精品久久美女| 亚洲激情精品| 日韩精品一级| 精品99久久| 日本不卡的三区四区五区| 欧美日本精品| 精品福利久久久| 成人美女视频| 日韩美女国产精品| 亚洲tv在线| 国产亚洲毛片在线| 日韩国产成人精品| 日韩欧美不卡| 亚洲激情久久| 日韩av中文在线观看| 日本国产一区| 日韩欧美三级| 欧美激情 亚洲a∨综合| 成人精品高清在线视频| 麻豆视频一区| 日韩欧美网址| 免费久久99精品国产自在现线| 亚洲免费高清| 亚洲精品影院在线观看| 国产专区一区| 黄色网一区二区| 蜜桃一区二区三区在线| 午夜宅男久久久| 国产日韩高清一区二区三区在线| 麻豆精品一区二区综合av| 国产一区二区三区不卡视频网站 | 日韩一区二区三区精品| 亚洲韩日在线| 免费一区二区视频| 日韩av网站在线免费观看| 欧美xxxx中国| 蜜桃成人av| 视频一区二区三区在线| 国产一区二区亚洲| 日韩久久一区二区三区| 久久国产电影| 国产香蕉精品| 亚洲日本久久| 国产精品一区二区三区美女 | 久久夜夜操妹子| 国产色综合网| 久久久国产精品网站| 性欧美长视频| 欧美在线资源| 岛国av在线网站| 欧美在线日韩| 国产中文字幕一区二区三区| 亚欧成人精品| 99视频精品全国免费| 日韩中文字幕麻豆| 国产成人a视频高清在线观看| 国户精品久久久久久久久久久不卡| 欧美日韩 国产精品| 黄色av一区| 新版的欧美在线视频| 国产福利亚洲| 青青草伊人久久| 模特精品在线| 香蕉国产精品| 久久影视一区| 日韩精品麻豆| 91欧美在线| 国产精品网址| 日韩中文字幕无砖| 9色精品在线| 99久久九九| 五月天av在线| 久久久久免费| 精品国产第一福利网站| 久久精品资源| 国产精品一页| 国产精品自在| 久久国际精品| 久久精品国产www456c0m| 国产字幕视频一区二区| 精品一区二区三区在线观看视频 | 精品久久精品| 亚洲精品亚洲人成在线观看| 亚洲人成亚洲精品| 99精品综合| 亚洲欧美日本国产专区一区| 国内亚洲精品| 亚洲成人av观看| 日韩毛片在线| 欧美在线观看视频一区| 久久中文字幕二区| 福利一区二区免费视频| 精品一区二区三区中文字幕 | 91麻豆国产自产在线观看亚洲| 国产精品调教| 久久99久久人婷婷精品综合| 国产精品片aa在线观看| 欧美国产极品| 成午夜精品一区二区三区软件| av在线最新| 高清av一区二区三区| 欧美一级精品| 国产精品日韩久久久| 国产精品日韩久久久| 亚洲制服一区| 国产一区 二区| 精品国产成人| 九色porny丨国产首页在线| 亚洲风情在线资源| 久久在线免费| 亚洲欧美日韩国产| 日韩不卡一二三区| 久久亚洲精精品中文字幕| 色乱码一区二区三区网站| 亚洲va中文在线播放免费| 亚洲国产专区校园欧美| 亚洲欧美日韩综合国产aⅴ| 欧美资源在线| 日韩精品国产欧美| 久久精品国产一区二区| 欧美不卡高清一区二区三区| 亚洲精品极品少妇16p| 91精品精品| 亚洲欧美网站在线观看| 亚洲综合色婷婷在线观看| 黄色aa久久| 欧美在线观看天堂一区二区三区| 国产精品xx| 国产高清亚洲| 亚州精品视频| 羞羞答答国产精品www一本| 日韩精品dvd| 伊人久久视频| 成人久久久久| 国产精品调教| 最新中文字幕在线播放 | 国产精品久久久久久久免费软件| 国产一区二区三区日韩精品| 亚洲精品国产嫩草在线观看 | 综合国产视频| 精品国产午夜| 悠悠资源网久久精品| 国产欧美自拍| 欧美成人日韩| 国产欧美日韩精品高清二区综合区 | 精品一区二区三区四区五区| 中国字幕a在线看韩国电影| 在线精品视频在线观看高清| 日韩高清电影免费| 国产精品久久久久av电视剧| 在线精品一区二区| 国产a亚洲精品| 日韩一区欧美二区| 成人日韩av| 亚洲天堂av资源在线观看| 成人国产精品久久| 四虎国产精品免费久久| 亚洲www免费| 国产精品主播| 欧美日韩精品一本二本三本| 日本不卡视频一二三区| 国产亚洲字幕| 福利一区视频| 国产一区日韩欧美| 热久久久久久久| 久久xxxx|