พื้นฐาน: มันต้องใช้แนวคิดการออกแบบเชิงวัตถุความคิด polymorphic และแนวคิดการสะท้อน
การเกิดขึ้นของกลไกพร็อกซีแบบไดนามิกของ Java ช่วยให้นักพัฒนา Java ได้รับคลาสพร็อกซีแบบไดนามิกโดยไม่ต้องเขียนคลาสพร็อกซีด้วยตนเอง คลาสพร็อกซีรับผิดชอบในการส่งการเรียกวิธีการทั้งหมดไปยังวัตถุผู้แทนเพื่อสะท้อนการดำเนินการ ในระหว่างกระบวนการดำเนินการจัดส่งนักพัฒนายังสามารถปรับวัตถุตัวแทนและฟังก์ชั่นได้ตามต้องการ นี่เป็นกรอบพร็อกซีที่ยืดหยุ่นและยืดหยุ่นมาก โดยการอ่านบทความนี้ผู้อ่านจะมีความเข้าใจที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับกลไกพร็อกซีแบบไดนามิก Java บทความนี้วิเคราะห์รหัสก่อนตามกลไกการทำงานและลักษณะของพร็อกซีแบบไดนามิก Java และหักค่าใช้จ่ายภายในของคลาสการสร้างแบบไดนามิก
แนวคิดพื้นฐานและการจำแนกประเภทของโมเดลตัวแทน
โหมดพร็อกซี: จัดเตรียมพร็อกซีสำหรับวัตถุอื่น ๆ เพื่อควบคุมการเข้าถึงวัตถุนี้ วัตถุพร็อกซีทำหน้าที่เป็นตัวกลางและสามารถลบบริการหรือเพิ่มบริการเพิ่มเติมหรืออ้างถึงอื่น ๆ : "คลาสพร็อกซีมีหน้าที่รับผิดชอบในการประมวลผลข้อความล่วงหน้าสำหรับคลาสผู้ได้รับมอบหมายข้อความกรองและข้อความส่งต่อและดำเนินการประมวลผลที่ตามมา
สถานการณ์แอปพลิเคชันของโหมดตัวแทนในการพัฒนา
พร็อกซีระยะไกล: จัดเตรียมวัตถุตัวแทน LAN สำหรับวัตถุในภูมิภาคทางภูมิศาสตร์ที่แตกต่างกัน
ตัวแทนเสมือนจริง: วัตถุล่าช้าที่ใช้ทรัพยากรจำนวนมากตามต้องการและสร้างมันขึ้นมาเมื่อพวกเขาต้องการจริงๆ ตัวอย่างเช่นข้อความจะปรากฏขึ้นก่อนจากนั้นรูปภาพจะปรากฏบนหน้าเว็บ
ตัวแทนการป้องกัน: ควบคุมสิทธิ์การเข้าถึงของผู้ใช้ที่แตกต่างกัน ตัวอย่างเช่น: หลังจากการลงทะเบียนลูกค้าสำเร็จแล้วสามารถเพิ่มลบแก้ไขและตรวจสอบการดำเนินการได้
ตัวแทนอ้างอิงอัจฉริยะ: ให้บริการเพิ่มเติมแก่ตัวแทนเป้าหมาย
วิธีการใช้โหมดพร็อกซี
อันไหนดีกว่าที่จะใช้พร็อกซีแบบไดนามิกโดยใช้มรดกและการรวมกัน!
อินเทอร์เฟซสาธารณะเคลื่อนย้ายได้ {โมฆะสาธารณะย้าย ();} รถคลาสสาธารณะใช้การเคลื่อนย้าย {@Override โมฆะสาธารณะย้าย () {ลอง {thread.sleep (สุ่มใหม่ (). nextint (1000)); system.out.println ("การขับขี่ ... "); car2 ขยายรถ {@Override โมฆะสาธารณะย้าย () {// แยกรหัสเพิ่มตรรกะธุรกิจเริ่มต้นใช้เวลานาน = System.currentTimeMillis (); system.out.println ("รถเริ่มขับ ... "); super.move () "+(endtime-starttime)+" ms ");}}วิธีการสืบทอดเพื่อใช้งานพร็อกซี
Moveablecar2=newCar2();
car2.move();
วิธีการรวมใช้พร็อกซี
Carcar=newCar();
Moveablem=newCar3(car);
m.move();
สรุป
วิธีการสืบทอดไม่ยืดหยุ่นเพียงพอ เมื่อฟังก์ชั่นซ้อนทับคุณสามารถขยายระดับพร็อกซีได้อย่างเต็มที่เท่านั้น
การใช้การรวมตัวตัวแทนสามารถส่งต่อซึ่งกันและกันและพร็อกซีสามารถรวมกันได้อย่างยืดหยุ่น
คลาสสาธารณะ CarlogProxy ขยายรถ {@Override โมฆะสาธารณะย้าย () {// แยกรหัสและเพิ่มตรรกะทางธุรกิจ LOGIC LONETIME = System.CurrentTimeMillis (); System.out.println ("เริ่มเข้าสู่ระบบ ... "); super.move () CartimeProxy ใช้การเคลื่อนย้าย {public cartimeProxy (รถยนต์รถยนต์) {super (); this.car = car;} carcar ส่วนตัว; @Override โมฆะการเคลื่อนย้ายสาธารณะ () {// แยกรหัสและเพิ่มตรรกะทางธุรกิจมานาน = System.currentTimeLis (); endtime = system.currentTimeMillis (); system.out.println ("รถปลายเพื่อขับ ... เวลา:"+(endtime-starttime)+"ms");}}@test: car car = new car (); cartimeproxy ctp = new cartimeproxy (carlog) ยังสามารถส่งผ่านอินสแตนซ์พร็อกซีต่อกันและกันผ่านอินเตอร์เฟส carlogproxy clp1 = ใหม่ carlogproxy (Car); cartimeproxy ctp1 = ใหม่ cartimeproxy (clp1); ctp1.move ();JDK Proxy Dynamic และ CGLIB Dynamic Proxy
พร็อกซีไดนามิก JDK
การใช้งานตัวแทน
ควรทำอย่างไรถ้าวัตถุต่าง ๆ ต้องการใช้คลาสพร็อกซีด้วยฟังก์ชั่นเดียวกัน?
ในเวลานี้คุณสามารถลองรวมเข้ากับพร็อกซีคลาสเดียวกัน ------- พร็อกซีแบบไดนามิก: การใช้พร็อกซีสำหรับคลาสที่แตกต่างกัน/วิธีการที่แตกต่างกัน;
กระบวนการทั่วไปมีดังนี้:
Java Dynamic Proxy Class ตั้งอยู่ภายใต้แพ็คเกจ java.lang.reflect ซึ่งโดยทั่วไปส่วนใหญ่เกี่ยวข้องกับสองคลาสต่อไปนี้:
(1) InterfaceInVocationHandler: มีเพียงวิธีเดียวเท่านั้นที่กำหนดไว้ในอินเตอร์เฟสนี้ PublicObjectInVoke (ObjectObj, MethodMethod, Object [] args)
OBJ: โดยทั่วไปหมายถึงคลาสพร็อกซี
วิธีการ: เป็นวิธีพร็อกซี
Args เป็นอาร์เรย์ของพารามิเตอร์สำหรับวิธีนี้
วิธีนามธรรมนี้ถูกนำไปใช้แบบไดนามิกในคลาสพร็อกซี
(2) พร็อกซี: คลาสนี้เป็นคลาสพร็อกซีแบบไดนามิก
statixObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)
ส่งคืนอินสแตนซ์ของคลาสไกลโคไซด์และคลาสพร็อกซีที่ส่งคืนสามารถใช้เป็นคลาสพร็อกซี (คุณสามารถใช้วิธีการที่ประกาศในอินเทอร์เฟซโดยคลาสพร็อกซี);
ตัวอย่างการใช้งาน:
@ TimeHandler คลาสสาธารณะ TimeHandler ใช้ InvocationHandler {Public TimeHandler (เป้าหมายวัตถุ) {super (); this.target = target;} private objectTarget;/******************************************************************* startTime = system.currentTimeMillis (); system.out.println ("รถเริ่มขับ ... "); method.invoke (เป้าหมาย); endtime ยาว = system.currentTimeMillis (); system.out.println ("รถสิ้นสุดลง ... @อินเทอร์เฟซของอินเทอร์เฟซสาธารณะคลาสพร็อกซีเคลื่อนย้ายได้ {โมฆะสาธารณะย้าย ();}@คลาสพร็อกซี รถคลาสสาธารณะใช้งานได้ {@Override โมฆะสาธารณะย้าย () {ลอง {thread.sleep (ใหม่สุ่ม (). nextint (1000)); system.out.println ("... การขับขี่ ... ");} catch (interruptedException e)@ทดสอบ
การทดสอบระดับสาธารณะ { / *** JDK Proxy Proxy Test Class* / โมฆะคงที่สาธารณะหลัก (String [] args) {Car Car = ใหม่ Car (); InvocationHandler H = ใหม่ TimeHandler (Car); คลาส <s?> cls = car.getClass (); /** loader class loader* อินเทอร์เฟซที่ใช้งานอินเตอร์เฟส* H InvocationHandler*/ Mowable M = (Mowable) Proxy.newProxyInstance (cls.getClassLoader (), cls.getInterfaces (), h); M.Move (); -&& ผลการทดสอบ
สรุป
DynamicProxy เป็นคลาส:
มันเป็นคลาสที่สร้างขึ้นเมื่อรันไทม์ คลาสนี้จำเป็นต้องใช้ชุดอินเทอร์เฟซ เมื่อใช้คลาสพร็อกซีแบบไดนามิกจะต้องใช้อินเทอร์เฟซ IntripationHandler
ขั้นตอนทั่วไปสำหรับ JDK Dynamic Proxy
1. สร้างคลาสที่ใช้อินเทอร์เฟซ raveCocationHandler ซึ่งจะต้องใช้ Invoke ()
2. สร้างคลาสพร็อกซีและอินเทอร์เฟซ
3. วิธีการโทรแบบคงที่ของ Proxy เพื่อสร้างคลาสพร็อกซี
newproxyinstance (classloaderloader, คลาส [] อินเทอร์เฟซ, InvocationHandlerh)
4. วิธีการโทรผ่านพร็อกซี
การดำเนินการตามพร็อกซีแบบไดนามิก CGLIB
การใช้งานตัวแทน
@introducing cglib-node-2.2.jar แพ็คเกจ
@CgLibProxy คลาสสกัดกั้นใช้อินเทอร์เฟซ MethodIntercept: การเขียนวิธีการสกัดกั้นใหม่
คลาสสาธารณะ CGLIBPROXY ใช้ MethodInterceptor {Private EnhancerenHancer = ใหม่ enhancer (); วัตถุสาธารณะ getProxy (คลาส Cl) {// ตั้งค่าคลาสที่สร้าง subclass enhancer.setsuperclass (cl); enhancer.setCallback (นี่) วิธีการ* วิธีการ args* อินสแตนซ์พร็อกซีของพร็อกซีคลาส**/@การแทนที่วัตถุสาธารณะ (Override Obj, Method M, Object [] args, Methodproxy Proxy) โยน {system.out.out.println ("เริ่มต้นเข้าสู่ระบบ ... "); จบ ... "); return null;}}@proxy Class Train
รถไฟระดับสาธารณะ {โมฆะสาธารณะย้าย () {system.out.println ("รถไฟกำลังขับรถ ... "); -@test class
การทดสอบระดับสาธารณะ { / *** cglibproxy proxy proxy class* / โมฆะคงที่สาธารณะหลัก (String [] args) {cglibproxy proxy = new cglibproxy (); Train T = (รถไฟ) Proxy.getProxy (Train.class); t.move (); -## ผลการทดสอบ:
สรุป
ขั้นตอนทั่วไปในการใช้งานพร็อกซีแบบไดนามิกโดยใช้ cglibproxy
1. สร้างคลาสเพื่อใช้งานอินเตอร์เฟส MethodInterceptor และแทนที่วิธีการสกัดกั้น
2. สร้างคลาสพร็อกซี
3. เรียกวิธีการที่กำหนดเองคลาสพร็อกซีเพื่อรับพร็อกซีอินสแตนซ์
4. เรียกวิธีการที่ต้องดำเนินการโดยพร็อกซีอินสแตนซ์
สรุปเปรียบเทียบ
พร็อกซีไดนามิก JDK
1. เฉพาะคลาสพร็อกซีที่ใช้อินเทอร์เฟซ
2. คลาสที่ไม่มีอินเทอร์เฟซไม่สามารถใช้งานพร็อกซีแบบไดนามิกสำหรับ JDK
พร็อกซีแบบไดนามิก CGLIB
1. การใช้งานพร็อกซีสำหรับชั้นเรียน
2. สร้างคลาสย่อยไปยังคลาสเป้าหมายการดำเนินการและใช้วิธีการสกัดกั้นเทคโนโลยีเพื่อสกัดกั้นวิธีการคลาสแม่ทั้งหมด
จำลองขั้นตอนการสร้างตัวแทน
แนวคิด:
ฟังก์ชั่นการใช้งาน: ส่งคืนวัตถุพร็อกซีผ่าน Newproxyinstance ของ Proxy
1. ประกาศซอร์สโค้ด (Agent Generation แบบไดนามิก)
2. รวบรวมซอร์สโค้ด (JDKCompilerapi) เพื่อสร้างคลาสใหม่ (คลาสพร็อกซี)
3. โหลดคลาสนี้ลงในหน่วยความจำและสร้างวัตถุใหม่ (วัตถุพร็อกซี)
4. ส่งคืนวัตถุพร็อกซี
ปรับปรุงการใช้งานพร็อกซีแบบไดนามิก
ก่อนอื่นเราได้รับคอมไพเลอร์ระบบรับตัวจัดการไฟล์ผ่านคอมไพเลอร์แล้วรับไฟล์ คอมไพเลอร์จะทำหน้าที่รวบรวม หลังจากทำการรวบรวมเสร็จแล้วเราจะโหลดไฟล์คลาสลงในคลาสโหลดเดอร์รับอินสแตนซ์ผ่านวิธีตัวสร้างจากนั้นเรียก Newinstance () เพื่อรับอินสแตนซ์ของวัตถุ
(1) รับคอมไพเลอร์ javacompilerCompiler = toolprovider.getSystemJavaCompiler ();
(2) File Manager StandardJavafileManagerFileMgr = Compiler.getStandardFileManager (NULL, NULL, NULL);
(3) รับไฟล์ iterableunits = fileMgr.getJavafileObjects (ชื่อไฟล์);
(4) การรวบรวมงาน CompilationTaskt = Compiler.getTask (null, filemgr, null, null, null, null, หน่วย);
(5) โหลดไปยังหน่วยความจำ
classloaderCl = classloader.getSystemClassLoader ();
classc = cl.loadclass ("com.imooc.proxy. $ proxy0");
(6) สร้างอินสแตนซ์ผ่านตัวสร้างของวัตถุพร็อกซี
constructorCtr = c.getConstructor (infce);
Ctr.NewInstance (newCar ());
-
ดังที่ได้กล่าวไว้ข้างต้นตรรกะธุรกิจภายในเป็นรหัสยาก วิธีการใช้งานพร็อกซีแบบไดนามิกจริงและตรรกะทางธุรกิจที่กำหนดแบบไดนามิก
1. คุณต้องสร้างโปรเซสเซอร์ธุรกรรม ก่อนอื่นคุณสร้างอินเทอร์เฟซนั่นคือ InchocationHandler เพื่อจำลอง JDK ชื่อของอินเทอร์เฟซนั้นเหมือนกับชื่อของโปรเซสเซอร์ธุรกรรม JDK นอกจากนี้คุณยังเขียนวิธีการที่เรียกว่า Invoke () ซึ่งใช้เพื่อแสดงวิธีการบางอย่างของวัตถุสำหรับการประมวลผลธุรกิจ ดังนั้นคุณต้องผ่านวัตถุบางอย่างและวิธีการของวัตถุเป็นพารามิเตอร์ของวิธีการเรียกใช้ () เรียกใช้ (ObjectObj, MethodMethod), วิธีการที่ใช้เป็นพารามิเตอร์ในการสะท้อน Java และต้องมีการแนะนำแพ็คเกจนี้ ด้วยวิธีนี้อินเทอร์เฟซ InvocationHandler จะเสร็จสมบูรณ์
2. สร้างคลาสการใช้งานการประมวลผลการทำธุรกรรมเช่น TimerProxy เพื่อใช้งานอินเตอร์เฟส InvocationHandler ดังนั้นโครงสร้างจะกลายเป็น
― ― ―― timerproxyimplementsInvocationHandler { - -------------------------------------------------------------------------------คุณต้องผ่านวัตถุเป้าหมายหากคุณไม่มีพารามิเตอร์คุณไม่สามารถเขียนพารามิเตอร์ได้ สร้างวิธีการก่อสร้างวัตถุพร็อกซีและเริ่มต้นวัตถุเป้าหมาย
3. ในเมธอด newproxyinstance () ของคลาสพร็อกซีนอกเหนือจากการใช้อินเทอร์เฟซคลาสเป้าหมายเป็นพารามิเตอร์คุณต้องผ่านการเรียกใช้โปรเซสเซอร์ธุรกรรมการเรียกร้องให้ทำธุรกรรมและจากนั้นเปลี่ยนส่วนที่มีรหัสยากของวัตถุอินสแตนซ์ที่สร้างขึ้นและใช้วิธีการประมวลผลธุรกรรมเพื่อแทนที่ ความยากลำบากอยู่ในการประกบของสตริง
สรุป
ในโครงการของเรารูปแบบตัวแทนมีความสำคัญในทางปฏิบัติ ตัวอย่างเช่นหากเราต้องการเรียกคลาสภายใต้แพ็คเกจ JAR บางอย่างเราสามารถเพิ่มตรรกะทางธุรกิจพิเศษบางอย่างก่อนที่จะโทรเข้าชั้นเรียนนี้ วิธีนี้เรียกว่าการเขียนโปรแกรมที่เน้น AOP (เพิ่มฟังก์ชั่นเพิ่มเติมโดยไม่ต้องเปลี่ยนฟังก์ชั่นดั้งเดิม)
ข้างต้นเป็นคำอธิบายโดยละเอียดทั้งหมดของรหัสพร็อกซี (รูปแบบการออกแบบ) ของ Java Dynamic ในบทความนี้ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน เพื่อนที่สนใจสามารถอ้างถึงหัวข้ออื่น ๆ ที่เกี่ยวข้องในเว็บไซต์นี้ต่อไป หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น ขอบคุณเพื่อนที่ให้การสนับสนุนเว็บไซต์นี้!