พร็อกซีไดนามิก Java
โหมดพร็อกซี
รูปแบบตัวแทนเป็นรูปแบบการออกแบบ Java ที่ใช้กันทั่วไป ลักษณะเฉพาะคือคลาสตัวแทนและคลาสผู้รับมอบสิทธิ์มีอินเทอร์เฟซเดียวกัน คลาสตัวแทนมีหน้าที่หลักในการประมวลผลข้อความล่วงหน้าสำหรับคลาสผู้รับมอบสิทธิ์ การกรองข้อความ การส่งต่อข้อความไปยังคลาสผู้รับมอบสิทธิ์ และประมวลผลข้อความในภายหลัง โดยปกติจะมีความเชื่อมโยงระหว่างคลาสพร็อกซีและคลาสผู้รับมอบสิทธิ์ วัตถุของคลาสพร็อกซีเชื่อมโยงกับวัตถุของคลาสผู้รับมอบสิทธิ์ วัตถุของคลาสพร็อกซีนั้นไม่ได้ใช้บริการจริง แต่โดยการเรียกวิธีการที่เกี่ยวข้อง ของวัตถุของคลาสผู้รับมอบสิทธิ์ ให้บริการเฉพาะ
ตามระยะเวลาการสร้างเอเจนต์ คลาสเอเจนต์สามารถแบ่งได้เป็น 2 ประเภท
พร็อกซีแบบคงที่: สร้างโดยโปรแกรมเมอร์หรือสร้างขึ้นโดยอัตโนมัติโดยเครื่องมือเฉพาะแล้วคอมไพล์ ก่อนที่โปรแกรมจะรัน ไฟล์ .class ของคลาสพร็อกซีนั้นมีอยู่แล้ว
พร็อกซีแบบไดนามิก: สร้างขึ้นแบบไดนามิกโดยใช้กลไกการสะท้อนเมื่อโปรแกรมกำลังทำงาน
ขั้นแรกให้ดูที่พร็อกซีแบบคงที่:
1.นับ.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.dao;
-
* กำหนดอินเทอร์เฟซบัญชี
-
* @ผู้เขียน ผู้ดูแลระบบ
-
-
ส่วนต่อประสานสาธารณะนับ {
// ดูวิธีการบัญชี
สาธารณะเป็นโมฆะ queryCount();
//แก้ไขวิธีการบัญชี
โมฆะสาธารณะ updateCount();
-
2. CountImpl.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.dao.impl;
นำเข้า net.battier.dao.Count;
-
* คลาสมอบหมาย (รวมถึงตรรกะทางธุรกิจ)
-
* @ผู้เขียน ผู้ดูแลระบบ
-
-
คลาสสาธารณะ CountImpl ดำเนินการนับ {
@แทนที่
โมฆะสาธารณะ queryCount() {
System.out.println("ดูวิธีการใช้บัญชี...");
-
@แทนที่
โมฆะสาธารณะ updateCount() {
System.out.println("แก้ไขวิธีการบัญชี...");
-
-
CountProxy.java
แพ็คเกจ net.battier.dao.impl;
นำเข้า net.battier.dao.Count;
-
* นี่คือคลาสพร็อกซี (คลาสการใช้งาน CountImpl ที่ปรับปรุงแล้ว)
-
* @ผู้เขียน ผู้ดูแลระบบ
-
-
คลาสสาธารณะ CountProxy ดำเนินการนับ {
ส่วนตัว CountImpl countImpl;
-
* แทนที่ตัวสร้างเริ่มต้น
-
* @param countImpl
-
CountProxy สาธารณะ (CountImpl countImpl) {
this.countImpl = countImpl;
-
@แทนที่
โมฆะสาธารณะ queryCount() {
System.out.println("ก่อนการประมวลผลธุรกรรม");
// เรียกเมธอดของคลาสผู้รับมอบสิทธิ์
countImpl.queryCount();
System.out.println("หลังการประมวลผลธุรกรรม");
-
@แทนที่
โมฆะสาธารณะ updateCount() {
System.out.println("ก่อนการประมวลผลธุรกรรม");
// เรียกเมธอดของคลาสผู้รับมอบสิทธิ์
countImpl.updateCount();
System.out.println("หลังการประมวลผลธุรกรรม");
-
-
3. TestCount.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.test;
นำเข้า net.battier.dao.impl.CountImpl;
นำเข้า net.battier.dao.impl.CountProxy;
-
*คลาสนับจำนวนการทดสอบ
-
* @ผู้เขียน ผู้ดูแลระบบ
-
-
TestCount ระดับสาธารณะ {
โมฆะคงที่สาธารณะ main (String [] args) {
CountImpl countImpl = ใหม่ CountImpl();
CountProxy countProxy = CountProxy ใหม่ (countImpl);
countProxy.updateCount();
countProxy.queryCount();
-
-
เมื่อสังเกตโค้ด คุณจะพบว่าแต่ละคลาสพร็อกซีสามารถให้บริการได้เพียงอินเทอร์เฟซเดียวเท่านั้น ด้วยวิธีนี้ จะมีการสร้างพรอกซีมากเกินไปในระหว่างการพัฒนาโปรแกรมอย่างหลีกเลี่ยงไม่ได้ ยิ่งกว่านั้น การดำเนินการของพร็อกซีทั้งหมดจะเหมือนกันยกเว้นวิธีที่พวกเขาเรียก จะต้องทำซ้ำในเวลานี้ วิธีที่ดีที่สุดในการแก้ปัญหานี้คือการใช้คลาสพร็อกซีเพื่อทำให้ฟังก์ชันพร็อกซีทั้งหมดเสร็จสมบูรณ์ ในกรณีนี้ ต้องใช้พร็อกซีแบบไดนามิกเพื่อทำให้เสร็จสมบูรณ์
มาดูพร็อกซีแบบไดนามิกกัน:
พร็อกซีไดนามิก JDK ประกอบด้วยคลาสและอินเทอร์เฟซ:
อินเทอร์เฟซ InvocationHandler:
InvocationHandler อินเทอร์เฟซสาธารณะ {
การเรียกใช้วัตถุสาธารณะ (พร็อกซีวัตถุ, วิธีการวิธีการ, วัตถุ [] args) พ่น Throwable;
-
คำอธิบายพารามิเตอร์:
พร็อกซีวัตถุ: หมายถึงวัตถุที่กำลังมอบฉันทะ
วิธีการ วิธีการ: วิธีการที่จะเรียก
Object[] args: พารามิเตอร์ที่จำเป็นเมื่อเรียกใช้เมธอด
คุณสามารถนึกถึงคลาสย่อยของอินเทอร์เฟซ InvocationHandler ว่าเป็นคลาสการดำเนินการสุดท้ายของพร็อกซี โดยแทนที่ ProxySubject
คลาสพร็อกซี:
คลาส Proxy เป็นคลาสที่เชี่ยวชาญด้านการดำเนินการของพร็อกซี คลาสนี้สามารถใช้เพื่อสร้างคลาสการใช้งานแบบไดนามิกสำหรับอินเทอร์เฟซตั้งแต่หนึ่งอินเทอร์เฟซขึ้นไป
วัตถุคงที่สาธารณะ newProxyInstance (ตัวโหลด ClassLoader, อินเตอร์เฟสคลาส <?> [],
InvocationHandler ซ)
พ่น IllegalArgumentException
คำอธิบายพารามิเตอร์:
ตัวโหลด ClassLoader: ตัวโหลดคลาส
อินเทอร์เฟซคลาส<?>[]: รับอินเทอร์เฟซทั้งหมด
InvocationHandler h: รับอินสแตนซ์คลาสย่อยของอินเทอร์เฟซ InvocationHandler
Ps: ตัวโหลดคลาส
จำเป็นต้องมีอินสแตนซ์ของคลาส ClassLoader ในเมธอด newProxyInstance() ในคลาส Proxy จริงๆ แล้ว ClassLoader สอดคล้องกับตัวโหลดคลาสหลักสามตัว
Booststrap ClassLoader: ตัวโหลดนี้เขียนด้วยภาษา C++ และไม่สามารถมองเห็นได้ในการพัฒนาทั่วไป
Extension ClassLoader: ใช้เพื่อโหลดคลาสส่วนขยาย โดยทั่วไปจะสอดคล้องกับคลาสในไดเร็กทอรี jre/lib/ext
AppClassLoader: (ค่าเริ่มต้น) โหลดคลาสที่ระบุโดย classpath ซึ่งเป็นตัวโหลดที่ใช้บ่อยที่สุด
พร็อกซีแบบไดนามิก
ตรงกันข้ามกับคลาสพร็อกซีแบบคงที่ มีคลาสพร็อกซีแบบไดนามิก รหัสไบต์ของคลาสพร็อกซีแบบไดนามิกถูกสร้างขึ้นแบบไดนามิกโดยกลไกการสะท้อนของ Java เมื่อโปรแกรมกำลังทำงาน โดยไม่จำเป็นต้องให้โปรแกรมเมอร์เขียนซอร์สโค้ดด้วยตนเอง คลาสพร็อกซีไดนามิกไม่เพียงทำให้งานการเขียนโปรแกรมง่ายขึ้น แต่ยังปรับปรุงความสามารถในการปรับขนาดของระบบซอฟต์แวร์ด้วย เนื่องจากกลไกการสะท้อนของ Java สามารถสร้างคลาสพร็อกซีไดนามิกประเภทใดก็ได้ คลาส Proxy และอินเทอร์เฟซ InvocationHandler ในแพ็คเกจ java.lang.reflect ให้ความสามารถในการสร้างคลาสพร็อกซีแบบไดนามิก
ตัวอย่างพร็อกซีแบบไดนามิก:
1.BookFacade.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.dao;
BookFacade อินเทอร์เฟซสาธารณะ {
addBook เป็นโมฆะสาธารณะ ();
-
2.BookFacadeImpl.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.dao.impl;
นำเข้า net.battier.dao.BookFacade;
BookFacadeImpl ระดับสาธารณะใช้ BookFacade {
@แทนที่
addBook เป็นโมฆะสาธารณะ () {
System.out.println("เพิ่มวิธีการจอง...");
-
-
BookFacadeProxy.java
แพ็คเกจ net.battier.proxy;
นำเข้า java.lang.reflect.InvocationHandler;
นำเข้า java.lang.reflect.Method;
นำเข้า java.lang.reflect.Proxy;
-
* คลาสพร็อกซีพร็อกซีไดนามิก JDK
-
* @ผู้เขียน นักเรียน
-
-
BookFacadeProxy ระดับสาธารณะใช้ InvocationHandler {
เป้าหมายวัตถุส่วนตัว
-
* ผูกวัตถุผู้รับมอบสิทธิ์และส่งคืนคลาสพร็อกซี
* @param เป้าหมาย
* @กลับ
-
การผูกวัตถุสาธารณะ (เป้าหมายวัตถุ) {
this.target = เป้าหมาย;
//รับวัตถุพร็อกซี
กลับ Proxy.newProxyInstance(target.getClass().getClassLoader()
target.getClass().getInterfaces() นี่); //ในการผูกอินเทอร์เฟซ (นี่เป็นข้อบกพร่อง cglib จะชดเชยข้อบกพร่องนี้)
-
@แทนที่
-
* วิธีการโทร
-
การเรียกใช้วัตถุสาธารณะ (พร็อกซีวัตถุ, วิธีการวิธีการ, วัตถุ [] args)
ขว้างได้ ขว้างได้ {
ผลลัพธ์ของวัตถุ = null;
System.out.println("สิ่งที่เริ่มต้น");
//วิธีการดำเนินการ
result=method.inurge(เป้าหมาย, args);
System.out.println("สิ้นสุดสิ่งต่าง ๆ");
ส่งคืนผลลัพธ์;
-
-
3. TestProxy.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.test;
นำเข้า net.battier.dao.BookFacade;
นำเข้า net.battier.dao.impl.BookFacadeImpl;
นำเข้า net.battier.proxy.BookFacadeProxy;
TestProxy ระดับสาธารณะ {
โมฆะคงที่สาธารณะ main (String [] args) {
พร็อกซี BookFacadeProxy = BookFacadeProxy ใหม่ ();
BookFacade bookProxy = (BookFacade) proxy.bind(BookFacadeImpl() ใหม่);
bookProxy.addBook();
-
-
อย่างไรก็ตาม พร็อกซีไดนามิกของ JDK ขึ้นอยู่กับการใช้งานอินเทอร์เฟซ หากบางคลาสไม่ได้ใช้อินเทอร์เฟซ พร็อกซี JDK จะไม่สามารถใช้ได้ ดังนั้นจึงต้องใช้พร็อกซีไดนามิก cglib
พร็อกซีไดนามิก Cglib
กลไกพร็อกซีแบบไดนามิกของ JDK สามารถทำได้เฉพาะคลาสพร็อกซีที่ใช้อินเทอร์เฟซเท่านั้น คลาสที่ไม่สามารถใช้อินเทอร์เฟซได้ ไม่สามารถใช้พร็อกซีแบบไดนามิกของ JDK สำหรับคลาสได้ แต่เนื่องจากมีการใช้การสืบทอด คลาสที่แก้ไขขั้นสุดท้ายจึงไม่สามารถมอบฉันทะได้
ตัวอย่าง
1.BookFacadeCglib.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.dao;
BookFacade อินเทอร์เฟซสาธารณะ {
addBook เป็นโมฆะสาธารณะ ();
-
2.BookCadeImpl1.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.dao.impl;
-
* นี่คือคลาสการใช้งานที่ไม่ได้ใช้อินเทอร์เฟซ
-
* @ผู้เขียน นักเรียน
-
-
BookFacadeImpl1 ระดับสาธารณะ {
addBook เป็นโมฆะสาธารณะ () {
System.out.println("วิธีการทั่วไปในการเพิ่มหนังสือ...");
-
-
3.BookFacadeProxy.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.proxy;
นำเข้า java.lang.reflect.Method;
นำเข้า net.sf.cglib.proxy.Enhancer;
นำเข้า net.sf.cglib.proxy.MethodInterceptor;
นำเข้า net.sf.cglib.proxy.MethodProxy;
-
* ใช้พร็อกซีไดนามิก cglib
-
* @ผู้เขียน นักเรียน
-
-
BookFacadeCglib คลาสสาธารณะใช้ MethodInterceptor {
เป้าหมายวัตถุส่วนตัว
-
* สร้างวัตถุพร็อกซี
-
* @param เป้าหมาย
* @กลับ
-
วัตถุสาธารณะ getInstance (เป้าหมายวัตถุ) {
this.target = เป้าหมาย;
ตัวเพิ่มประสิทธิภาพ ตัวเพิ่มประสิทธิภาพ = ตัวเพิ่มประสิทธิภาพใหม่();
เพิ่ม.setSuperclass(this.target.getClass());
// วิธีการโทรกลับ
เพิ่ม.setCallback(นี้);
//สร้างวัตถุพร็อกซี
กลับตัวเพิ่มประสิทธิภาพ.สร้าง();
-
@แทนที่
// วิธีการโทรกลับ
การสกัดกั้นวัตถุสาธารณะ (Object obj, วิธีการวิธีการ, Object [] args,
พร็อกซี MethodProxy) พ่น Throwable {
System.out.println("สิ่งที่เริ่มต้น");
พรอกซี.invoidSuper(obj, args);
System.out.println("สิ้นสุดสิ่งต่าง ๆ");
กลับเป็นโมฆะ;
-
-
4. TestCglib.java
คัดลอกรหัสรหัส ดังต่อไปนี้:
แพ็คเกจ net.battier.test;
นำเข้า net.battier.dao.impl.BookFacadeImpl1;
นำเข้า net.battier.proxy.BookFacadeCglib;
TestCglib คลาสสาธารณะ {
โมฆะคงที่สาธารณะ main (String [] args) {
BookFacadeCglib cglib=ใหม่ BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(BookFacadeImpl1() ใหม่);
bookCglib.addBook();
-
-