หากคลาสพร็อกซีมีอยู่แล้วก่อนที่โปรแกรมจะทำงานแล้ววิธีพร็อกซีนี้เรียกว่าพร็อกซีแบบคงที่ ในกรณีนี้คลาสพร็อกซีมักจะกำหนดไว้ในรหัส Java โดยปกติคลาสพร็อกซีและคลาสตัวแทนในพร็อกซีแบบคงที่ใช้อินเทอร์เฟซเดียวกันหรือได้มาจากคลาสแม่เดียวกัน
1. ภาพรวม
1. ตัวแทนคืออะไร
เราทุกคนรู้ว่าตัวแทน WeChat เป็นเพียงการขายสินค้าในนามของผู้ผลิตและตัวแทน "ผู้ผลิต" มอบหมาย "ตัวแทนขายสินค้าให้พวกเขา เกี่ยวกับตัวแทนธุรกิจ WeChat ก่อนอื่นเมื่อเราซื้อสิ่งต่าง ๆ จากพวกเขาเรามักจะไม่รู้ว่าใครคือผู้ผลิตนั่นคือ "ผู้บัญชาการ" นั้นมองไม่เห็นเรา ประการที่สองตัวแทนธุรกิจ WeChat ส่วนใหญ่กำหนดเป้าหมายผู้คนในวงกลมของเพื่อนในฐานะลูกค้าของพวกเขาซึ่งเทียบเท่ากับ "ตัวกรอง" ของกลุ่มลูกค้าสำหรับผู้ผลิต เราเป็นนามธรรมเพิ่มเติมเกี่ยวกับตัวแทนและผู้ผลิตธุรกิจขนาดเล็ก อดีตสามารถเป็นนามธรรมเป็นคลาสตัวแทนและหลังสามารถถูกสรุปเป็นคลาสตัวแทน (คลาสตัวแทน) โดยการใช้พร็อกซีมักจะมีข้อดีสองประการและสามารถสอดคล้องกับสองลักษณะของตัวแทนธุรกิจขนาดเล็กที่เรากล่าวถึง:
ข้อได้เปรียบ 1: มันสามารถซ่อนการใช้งานของชั้นผู้แทน;
ข้อได้เปรียบ 2: มันสามารถบรรลุการแยกระหว่างไคลเอนต์และคลาสตัวแทนและสามารถทำการประมวลผลเพิ่มเติมได้โดยไม่ต้องแก้ไขรหัสคลาสตัวแทน
2. พร็อกซีแบบคงที่
หากคลาสพร็อกซีมีอยู่แล้วก่อนที่โปรแกรมจะทำงานแล้ววิธีพร็อกซีนี้เรียกว่าพร็อกซีแบบคงที่ ในกรณีนี้คลาสพร็อกซีมักจะกำหนดไว้ในรหัส Java โดยปกติคลาสพร็อกซีและคลาสตัวแทนในพร็อกซีแบบคงที่ใช้อินเทอร์เฟซเดียวกันหรือได้มาจากคลาสแม่เดียวกัน ด้านล่างเราใช้คลาสผู้ขายเพื่อเป็นตัวแทนของผู้ผลิตและคลาสธุรกิจเพื่อเป็นตัวแทนตัวแทนธุรกิจขนาดเล็กเพื่อแนะนำการใช้งานตัวแทนแบบคงที่อย่างง่าย ทั้งคลาสตัวแทนและคลาสพร็อกซีใช้อินเทอร์เฟซขาย คำจำกัดความของอินเทอร์เฟซการขายมีดังนี้:
อินเทอร์เฟซสาธารณะขาย {void sell (); เป็นโมฆะโฆษณา (); } คำจำกัดความของคลาสผู้ขายมีดังนี้: ผู้ขายระดับสาธารณะใช้การขาย {โมฆะสาธารณะขาย () {system.out.println ("ในวิธีการขาย"); } โฆษณาโมฆะสาธารณะ () {system, out.println ("วิธีการโฆษณา")}} คำจำกัดความของพร็อกซีคลาส BusinessAgent มีดังนี้:
ผู้ขายระดับสาธารณะใช้ขาย {โมฆะสาธารณะขาย () {system.out.println ("ในวิธีการขาย"); } โฆษณาโมฆะสาธารณะ () {system, out.println ("วิธีการโฆษณา")}} จากคำจำกัดความของคลาสธุรกิจเราสามารถเข้าใจได้ว่าตัวแทนคงที่สามารถนำไปใช้ได้ผ่านการรวมกันเพื่อให้คลาสตัวแทนสามารถอ้างอิงถึงคลาสผู้ได้รับมอบหมาย
ลองพิจารณาข้อกำหนดนี้ด้านล่าง: เพิ่มฟังก์ชั่นการกรองให้กับชั้นเรียนของผู้ขายและขายสินค้าให้กับนักศึกษาเท่านั้น ผ่านพร็อกซีแบบคงที่เราสามารถทำได้โดยไม่ต้องแก้ไขรหัสของคลาสผู้ขาย เราเพียงแค่ต้องเพิ่มคำพิพากษาให้กับวิธีการขายในชั้นเรียนธุรกิจและอาจเป็นดังนี้:
BusinessAgent ในชั้นเรียนสาธารณะขาย {... โมฆะสาธารณะขาย () {ถ้า (iscollegestudent ()) {ผู้ขาย. sell (); - สิ่งนี้สอดคล้องกับข้อได้เปรียบที่สองของการใช้พร็อกซีที่กล่าวถึงข้างต้น: มันสามารถบรรลุการแยกระหว่างไคลเอนต์และคลาสตัวแทนและสามารถทำการประมวลผลเพิ่มเติมได้โดยไม่ต้องแก้ไขรหัสคลาสตัวแทน ข้อ จำกัด ของพร็อกซีแบบคงที่คือคุณต้องเขียนคลาสพร็อกซีก่อนที่จะทำงาน มามุ่งเน้นไปที่การแนะนำวิธีพร็อกซีแบบไดนามิกในการสร้างคลาสพร็อกซีที่รันไทม์
2. ตัวแทนไดนามิก
1. พร็อกซีแบบไดนามิกคืออะไร
เมธอดพร็อกซีที่สร้างขึ้นโดยคลาสพร็อกซีเมื่อโปรแกรมเรียกใช้เรียกว่าพร็อกซีแบบไดนามิก นั่นคือในกรณีนี้คลาสพร็อกซีไม่ได้กำหนดไว้ในรหัส Java แต่ถูกสร้างขึ้นแบบไดนามิกที่รันไทม์ตาม "คำแนะนำ" ของเราในรหัส Java เมื่อเทียบกับพร็อกซีแบบคงที่ข้อได้เปรียบของพร็อกซีแบบไดนามิกคือมันสามารถจัดการฟังก์ชั่นของพร็อกซีคลาสได้อย่างสม่ำเสมอโดยไม่ต้องปรับเปลี่ยนฟังก์ชั่นของแต่ละคลาสพร็อกซี นี่เป็นนามธรรมมากขึ้น ลองรวมตัวอย่างเพื่อแนะนำว่าข้อดีของพร็อกซีแบบไดนามิกสะท้อนให้เห็นอย่างไร
ตอนนี้สมมติว่าเราต้องการใช้ข้อกำหนด: เอาต์พุต "ก่อน" ก่อนที่จะดำเนินการวิธีการในคลาสตัวแทนและเอาต์พุต "หลังจาก" หลังจากดำเนินการ เราจะแนะนำคลาสผู้ขายเป็นคลาสผู้ได้รับมอบหมายในตัวอย่างข้างต้นและคลาสธุรกิจเป็นคลาสพร็อกซี ก่อนอื่นให้ใช้พร็อกซีแบบคงที่เพื่อให้บรรลุข้อกำหนดนี้ รหัสที่เกี่ยวข้องมีดังนี้:
BusinessAgent ในชั้นเรียนสาธารณะขาย {ผู้ขายเอกชน mvendor; ธุรกิจสาธารณะ (ผู้ขายผู้ขาย) {this.mvendor = ผู้ขาย; } โมฆะสาธารณะขาย () {system.out.println ("ก่อน"); mvendor.sell (); System.out.println ("After"); } โฆษณาโมฆะสาธารณะ () {system.out.println ("ก่อน"); mvendor.ad (); System.out.println ("After"); - จากรหัสข้างต้นเราสามารถเข้าใจได้ว่าการใช้ความต้องการของเราผ่านพร็อกซีแบบคงที่ต้องการให้เราเพิ่มตรรกะที่สอดคล้องกันในแต่ละวิธี มีเพียงสองวิธีเท่านั้นที่นี่ดังนั้นภาระงานจึงไม่ใหญ่ จะเกิดอะไรขึ้นถ้าอินเทอร์เฟซขายมีหลายร้อยวิธี? ในเวลานี้การใช้พร็อกซีแบบคงที่จะเขียนรหัสซ้ำซ้อนจำนวนมาก ด้วยการใช้พร็อกซีแบบไดนามิกเราสามารถสร้าง "ตัวบ่งชี้ที่เหมือนกัน" เพื่อประมวลผลวิธีการทั้งหมดของคลาสพร็อกซีทั้งหมดโดยไม่ต้องแก้ไขแต่ละวิธีทีละวิธี มาแนะนำวิธีใช้พร็อกซีแบบไดนามิกเพื่อใช้ความต้องการของเรา
2. ใช้พร็อกซีแบบไดนามิก
(1) เมื่อใช้พร็อกซีแบบไดนามิกในอินเทอร์เฟซ InvocationHandler เราจำเป็นต้องกำหนดคลาสตัวกลางที่อยู่ระหว่างคลาสพร็อกซีและคลาสตัวแทน คลาสตัวกลางนี้จำเป็นต้องใช้อินเทอร์เฟซ InvocationHandler คำจำกัดความของอินเทอร์เฟซนี้มีดังนี้:
อินเทอร์เฟซ Public InvocationHandler {Object revoke (พร็อกซีวัตถุวิธีวิธีการวัตถุ [] args); - จากชื่อ InvocationHandler เราสามารถรู้ได้ว่าคลาสการไกล่เกลี่ยที่ใช้อินเทอร์เฟซนี้ใช้เป็น "ตัวประมวลผลการโทร" เมื่อเราเรียกวิธีการของวัตถุพร็อกซีคลาส "การโทร" นี้จะถูกส่งต่อไปยังเมธอด Invoke วัตถุพร็อกซีคลาสถูกส่งผ่านเป็นพารามิเตอร์พร็อกซี วิธีพารามิเตอร์ระบุวิธีที่เราเรียกว่าคลาสพร็อกซี Args เป็นพารามิเตอร์ของวิธีนี้ ด้วยวิธีนี้การโทรไปยังทุกวิธีในคลาสพร็อกซีจะกลายเป็นเรียกร้องให้เรียกใช้ดังนั้นเราสามารถเพิ่มตรรกะการประมวลผลแบบครบวงจรลงในวิธีการเรียกใช้ (หรือวิธีการต่าง ๆ ของคลาสพร็อกซีสามารถประมวลผลได้ตามพารามิเตอร์วิธีการ) ดังนั้นเราจำเป็นต้องส่งออก "ก่อน" ในการใช้วิธีการเรียกใช้คลาสการไกล่เกลี่ยจากนั้นเรียกใช้วิธีการเรียกของคลาสตัวแทนจากนั้นส่งออก "หลังจาก" มาใช้ทีละขั้นตอน
(2) ภายใต้วิธีพร็อกซีแบบไดนามิกของคลาสตัวแทนคลาสผู้แทนจะต้องใช้อินเทอร์เฟซที่แน่นอน ที่นี่เราใช้อินเทอร์เฟซขาย คำจำกัดความของคลาสผู้ขายมีดังนี้:
ผู้ขายระดับสาธารณะใช้ขาย {โมฆะสาธารณะขาย () {system.out.println ("ในวิธีการขาย"); } โฆษณาโมฆะสาธารณะ () {system, out.println ("วิธีการโฆษณา")}} (3) คลาสการไกล่เกลี่ยตามที่กล่าวไว้ข้างต้นคลาสการไกล่เกลี่ยจะต้องใช้อินเทอร์เฟซ InvocationHandler เป็นตัวประมวลผลการโทร "Intercept" เรียกใช้วิธีการระดับพร็อกซี คำจำกัดความของคลาสตัวกลางมีดังนี้:
คลาสสาธารณะ DynamicProxy ใช้ InvocationHandler {วัตถุส่วนตัว obj; // OBJ เป็นวัตถุระดับผู้แทน; DynamicProxy สาธารณะ (Object obj) {this.obj = obj; } @Override วัตถุสาธารณะเรียกใช้ (พร็อกซีวัตถุ, วิธีการ, วัตถุ [] args) โยน {system.out.println ("ก่อน"); Object result = method.invoke (obj, args); System.out.println ("After"); ผลการกลับมา; - จากรหัสข้างต้นเราจะเห็นว่าคลาสตัวกลางมีการอ้างอิงวัตถุตัวแทนและวิธีการที่สอดคล้องกันของวัตถุตัวแทนถูกเรียกในวิธีการเรียกใช้ (บรรทัดที่ 11) คุณคิดว่ามันดูคุ้นเคยเมื่อคุณเห็นสิ่งนี้หรือไม่? ถือการอ้างอิงวัตถุผู้แทนผ่านวิธีการรวมในที่สุดการแปลงการโทรภายนอกทั้งหมดทั้งหมดเพื่อเรียกใช้การโทรไปยังวัตถุผู้แทน นี่ไม่ใช่วิธีการใช้งานของพร็อกซีแบบคงที่ที่เราแนะนำข้างต้นหรือไม่? ในความเป็นจริงคลาสตัวกลางและระดับผู้แทนจะสร้างความสัมพันธ์แบบพร็อกซีแบบคงที่ ในความสัมพันธ์นี้คลาสตัวกลางเป็นคลาสพร็อกซีและคลาสผู้ได้รับมอบหมายเป็นคลาสการมอบหมาย พร็อกซีคลาสและคลาสตัวกลางยังก่อให้เกิดความสัมพันธ์พร็อกซีแบบคงที่ ในความสัมพันธ์นี้คลาสตัวกลางเป็นคลาสตัวแทนและคลาสพร็อกซีเป็นคลาสพร็อกซี กล่าวอีกนัยหนึ่งความสัมพันธ์ของพร็อกซีแบบไดนามิกประกอบด้วยความสัมพันธ์ของพร็อกซีแบบคงที่สองชุดซึ่งเป็นหลักการของพร็อกซีแบบไดนามิก มาแนะนำวิธีการ "สั่ง" สร้างคลาสพร็อกซีแบบไดนามิก
(4) รหัสพร็อกซีรุ่นพร็อกซีการสร้างแบบไดนามิกแบบไดนามิกการสร้างคลาสพร็อกซีคลาสที่เกี่ยวข้องมีดังนี้:
คลาสสาธารณะหลัก {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {// สร้างอินสแตนซ์ของคลาสการไกล่เกลี่ย DynamicProxy inter = ใหม่ DynamicProxy (ผู้ขายใหม่ ()); // เพิ่มประโยคนี้จะสร้างไฟล์ $ proxy0.class ซึ่งเป็นไฟล์ระดับพร็อกซีคลาสที่สร้างขึ้นแบบไดนามิกระบบ GetProperties () ใส่ ("sun.misc.proxygenerator.savegeneratedfiles", "true"); // รับพร็อกซีคลาสอินสแตนซ์ขายขาย = (ขาย) (proxy.newproxyinstance (sell.class.getClassLoader (), คลาสใหม่ [] {sell.class}, inter)); // การเรียกใช้เมธอดพร็อกซีคลาสผ่านวัตถุพร็อกซีคลาสจะไปที่วิธีการเรียกร้องให้เรียกขาย sell.sell (); sell.ad (); - ในรหัสข้างต้นเราเรียกวิธีการใหม่ของ Newproxyinstance ของคลาสพร็อกซีเพื่อรับอินสแตนซ์ของคลาสพร็อกซี คลาสพร็อกซีนี้ใช้อินเทอร์เฟซที่เราระบุและจะแจกจ่ายการเรียกวิธีการไปยังโปรเซสเซอร์การโทรที่ระบุ การประกาศวิธีการนี้มีดังนี้:
คัดลอกรหัสดังต่อไปนี้: วัตถุคงที่สาธารณะ newproxyinstance (classloader loader, คลาส <?> [] อินเตอร์เฟส, intriacationhandler h) พ่น unledalargumentException
พารามิเตอร์สามตัวของวิธีนี้มีดังนี้:
โหลดเดอร์: กำหนดคลาสของคลาสพร็อกซี;
อินเทอร์เฟซ: รายการอินเทอร์เฟซที่ใช้โดย Proxy Class
H: เรียกโปรเซสเซอร์นั่นคืออินสแตนซ์ของคลาสที่เรากำหนดไว้ข้างต้นซึ่งใช้อินเตอร์เฟส InchocationHandler ลองเรียกใช้เพื่อดูว่าพร็อกซีแบบไดนามิกของเราสามารถทำงานได้อย่างถูกต้องหรือไม่ เอาต์พุตที่ฉันทำงานที่นี่คือ:
นี่แสดงให้เห็นว่าพร็อกซีแบบไดนามิกของเราทำงานได้อย่างแน่นอน
เราได้กล่าวถึงหลักการของพร็อกซีแบบไดนามิกสั้น ๆ ข้างต้น ที่นี่เราจะสรุปสั้น ๆ : ก่อนอื่นเราสามารถรับอินสแตนซ์คลาสพร็อกซีผ่านวิธีการใหม่ของ Newproxyinstance จากนั้นเราสามารถเรียกวิธีการคลาสพร็อกซีผ่านอินสแตนซ์ของคลาสพร็อกซีนี้ ในเมธอดพร็อกซีคลาสเราจะเรียกวิธีการเรียกใช้ของคลาสการไกล่เกลี่ย (เรียกว่าโปรเซสเซอร์) ในเมธอด Invoke เราเรียกวิธีการที่สอดคล้องกันของคลาสตัวแทนและสามารถเพิ่มตรรกะการประมวลผลของเราเอง
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น