หากต้องการพร็อกซีคลาสด้วยพร็อกซีไดนามิก jdk คลาสพร็อกซีต้องใช้อินเทอร์เฟซอย่างน้อยหนึ่งอินเทอร์เฟซ และมีเพียงวิธีการในอินเทอร์เฟซเท่านั้นที่สามารถใช้พรอกซีได้
การใช้งานไดนามิกพร็อกซีใน jdk โดยทั่วไปจะแบ่งออกเป็นสามขั้นตอน:
1. เขียนอินเทอร์เฟซและคลาสการใช้งาน
2. เขียนโปรเซสเซอร์ที่ใช้อินเทอร์เฟซ InvocationHandler อินเทอร์เฟซนี้มีเพียงวิธีเดียวเท่านั้น และลายเซ็นของมันคือสาธารณะ การเรียกใช้วัตถุ (พร็อกซีวัตถุ วิธีการวิธีการ วัตถุ [] args)
พ่น Throwable คุณสามารถเพิ่มรหัสของคุณเองในวิธีการดำเนินการของโปรเซสเซอร์ก่อนและหลังการเรียกวิธีการเพื่อทำการสกัดกั้นแบบไดนามิก ควรสังเกตว่าคลาสพร็อกซีแบบไดนามิกที่สร้างโดยพร็อกซีไม่ใช่คลาสจริงที่เรากำลังพร็อกซี ดังนั้นเราจึงสามารถเพิ่มตัวแปรสมาชิกประเภท Object ไปยังโปรเซสเซอร์เพื่อชี้ไปยังคลาสที่เราต้องการให้เป็นพร็อกซีจริงๆ (นั่นคือ คลาสในคลาสการใช้งานขั้นตอนที่ 1)
3. ใช้เมธอด newProxyInstance ของคลาส java.lang.reflect.Proxy เพื่อสร้างคลาสพร็อกซีแบบไดนามิก สำหรับการเรียกเมธอดพร็อกซีทั้งหมด คุณสามารถเรียกเมธอดของคลาสพร็อกซีไดนามิกที่สร้างขึ้นได้โดยตรง แต่ก่อนอื่นคุณต้องทำการแปลงประเภทบังคับและแปลงเป็นอินเทอร์เฟซของวิธีที่เราต้องการเรียก
การวิเคราะห์หลักการของ JDK:
ด้วยการวิเคราะห์ซอร์สโค้ดของ Proxy คุณสามารถดูการสร้างคลาสพร็อกซีไดนามิกโดยละเอียดได้ เมธอด newProxyInstance จะสร้างคลาสอินสแตนซ์ของคลาสพร็อกซีแบบไดนามิกก่อน จากนั้นจึงเรียกตัวสร้างที่มีประเภทพารามิเตอร์เป็น InvocationHandler เพื่อสร้างคลาสพร็อกซีแบบไดนามิกและส่งคืน
จะสร้างอินสแตนซ์คลาสของคลาสพร็อกซีไดนามิกได้อย่างไร คลาส ProxyGenerator ใช้เพื่อสร้างคลาสไบต์สตรีมของคลาสพร็อกซีไดนามิกและโหลดลงในพื้นที่วิธีการ
การวิเคราะห์กระบวนการสร้างสตรีมคลาสไบต์ เราจะเห็นว่ามันใช้ Proxy เป็นคลาสพาเรนต์เพื่อใช้วิธีการทั้งหมดของอินเทอร์เฟซที่จะรับพรอกซี เนื้อความการใช้งานของแต่ละวิธีส่วนใหญ่จะเรียกวิธีการเรียกใช้ของโปรเซสเซอร์
รหัสหลักของกระบวนการสร้างสตรีมคลาสไบต์มีดังนี้:
คัดลอกรหัสรหัส ดังต่อไปนี้:
ไบต์ส่วนตัว [] GenerateClassFile()
-
addProxyMethod(hashCodeMethod, java/lang/Object);
addProxyMethod (เท่ากับวิธีการ, java/lang/Object);
addProxyMethod(toStringMethod, java/lang/Object);
สำหรับ (int i = 0; i < interfaces.length; i++)
-
วิธีการ amethod[] = interfaces[i].getMethods();
สำหรับ (int k = 0; k < amethod.length; k++)
addProxyMethod (amethod [k], อินเตอร์เฟส [i]);
-
รายการ;
สำหรับ (ตัววนซ้ำ ตัววนซ้ำ = proxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
รายการ = (รายการ) iterator.next ();
พยายาม
-
วิธีการเพิ่ม (สร้างตัวสร้าง ());
สำหรับ (ตัววนซ้ำ iterator1 = proxyMethods.values().iterator(); iterator1.hasNext();)
-
รายการ list1 = (รายการ)iterator1.next();
ตัววนซ้ำ iterator2 = list1.iterator();
ในขณะที่ (iterator2.hasNext ())
-
ProxyMethod proxymethod = (ProxyMethod)iterator2.next();
fields.add(FieldInfo ใหม่(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
วิธีการ.เพิ่ม(<SPAN style="COLOR: red">proxymethod.generateMethod()</SPAN><SPAN style="COLOR: #000000">);</SPAN>
คัดลอกรหัสรหัส ดังต่อไปนี้:
-
-
วิธีการเพิ่ม(generateStaticInitializer());
-
จับ (IOException ioException)
-
โยน InternalError ใหม่ ("ข้อยกเว้น I/O ที่ไม่คาดคิด");
-
ถ้า(methods.size() > 65535)
โยน IllegalArgumentException ใหม่ ("เกินขีดจำกัดวิธีการ");
ถ้า (fields.size() > 65535)
โยน IllegalArgumentException ใหม่ ("เกินขีดจำกัดฟิลด์");
cp.getClass(dotToSlash(ชื่อคลาส));
cp.getClass("java/lang/reflect/Proxy");
สำหรับ (int j = 0; j < interfaces.length; j ++)
cp.getClass(dotToSlash(อินเทอร์เฟซ[j].getName()));
cp.setReadOnly();
ByteArrayOutputStream bytearrayoutputstream = ใหม่ ByteArrayOutputStream();
DataOutputStream dataoutputstream = DataOutputStream ใหม่ (bytearrayoutputstream);
พยายาม
-
dataoutputstream.writeInt(-889275714);
dataoutputstream.writeShort(0);
dataoutputstream.writeShort(49);
cp.write(กระแสข้อมูลส่งออก);
dataoutputstream.writeShort(49);
dataoutputstream.writeShort(cp.getClass(dotToSlash(className)));
dataoutputstream.writeShort(cp.getClass("java/lang/reflect/Proxy"));
dataoutputstream.writeShort (อินเทอร์เฟซความยาว);
สำหรับ (int l = 0; l < interfaces.length; l++)
dataoutputstream.writeShort(cp.getClass(dotToSlash(อินเทอร์เฟซ[l].getName())));
dataoutputstream.writeShort(เขตข้อมูลขนาด());
ข้อมูลฟิลด์ข้อมูล ข้อมูลฟิลด์;
คัดลอกรหัสรหัส ดังต่อไปนี้:
//เพิ่มคุณสมบัติ
สำหรับ (ตัววนซ้ำ iterator3 = fields.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo)iterator3.next();
//เพิ่มวิธีการ
dataoutputstream.writeShort(วิธีขนาด());
ข้อมูลวิธีการ วิธีการข้อมูล;
สำหรับ (ตัววนซ้ำ iterator4 = method.iterator (); iterator4.hasNext (); methodinfo.write (dataoutputstream))
methodinfo = (MethodInfo)iterator4.next();
dataoutputstream.writeShort(0);
-
จับ (IOException ioException1)
-
โยน InternalError ใหม่ ("ข้อยกเว้น I/O ที่ไม่คาดคิด");
-
กลับ bytearrayoutputstream.toByteArray();
-
หมายเหตุ: ส่วนสีแดงในโค้ด proxymethod.generateMethod() จะสร้างเนื้อความของวิธีการสำหรับแต่ละวิธี เมื่อดูที่ซอร์สโค้ด เราจะเห็นว่าพวกเขากำลังเรียกวิธีการเรียกใช้ของตัวประมวลผลการใช้งานของอินเทอร์เฟซ InvocationHandler