ในกรณีของProxy Design Patternความแตกต่างระหว่างDynamic Proxy ของ JDK กับ API การสร้างรหัสแบบไดนามิกของบุคคลที่สามเช่นCGLibคืออะไร?
อะไรคือความแตกต่างระหว่างการใช้ทั้งสองวิธีและเมื่อใดที่ควรจะใช้มากกว่ากัน?
ในกรณีของProxy Design Patternความแตกต่างระหว่างDynamic Proxy ของ JDK กับ API การสร้างรหัสแบบไดนามิกของบุคคลที่สามเช่นCGLibคืออะไร?
อะไรคือความแตกต่างระหว่างการใช้ทั้งสองวิธีและเมื่อใดที่ควรจะใช้มากกว่ากัน?
คำตอบ:
พร็อกซี JDK Dynamic สามารถใช้พร็อกซีได้เฉพาะตามอินเทอร์เฟซ (ดังนั้นคลาสเป้าหมายของคุณต้องใช้อินเทอร์เฟซ
CGLIB (และ javassist) สามารถสร้างพร็อกซีได้โดยการทำคลาสย่อย ในสถานการณ์สมมตินี้พร็อกซีกลายเป็นคลาสย่อยของคลาสเป้าหมาย ไม่จำเป็นต้องมีอินเตอร์เฟส
ดังนั้นผู้รับมอบฉันทะ Java Dynamic สามารถ proxy: public class Foo implements iFoo
ที่ CGLIB สามารถ proxy:public class Foo
แก้ไข:
ฉันควรพูดถึงว่าเพราะ javassist และ CGLIB ใช้ proxy โดย subclassing นี่คือเหตุผลที่คุณไม่สามารถประกาศวิธีสุดท้ายหรือทำให้คลาสสุดท้ายเมื่อใช้เฟรมเวิร์กที่อาศัยสิ่งนี้ สิ่งนี้จะหยุดไลบรารีเหล่านี้ไม่ให้อนุญาตคลาสย่อยของคุณและแทนที่วิธีการของคุณ
ความแตกต่างในฟังก์ชั่น
ผู้รับมอบฉันทะ JDK อนุญาตให้มีการใช้ชุดของการเชื่อมต่อใด ๆ ในขณะ Object
subclassing วิธีการอินเตอร์เฟซใดบวกObject::hashCode
, Object::equals
และจะถูกส่งต่อไปยังแล้วObject::toString
InvocationHandler
นอกจากนี้ยังjava.lang.reflect.Proxy
มีการใช้อินเตอร์เฟสไลบรารีมาตรฐาน
cglib ช่วยให้คุณสามารถใช้ชุดของอินเทอร์เฟซใด ๆ ในขณะที่คลาสย่อยที่ไม่ใช่คลาสสุดท้าย นอกจากนี้วิธีการสามารถแทนที่เลือกเช่นไม่ใช่วิธีการที่เป็นนามธรรมทั้งหมดจะต้องมีการสกัดกั้น นอกจากนี้ยังมีวิธีการใช้วิธีที่แตกต่างกัน นอกจากนี้ยังมีInvocationHandler
ระดับ (ในแพคเกจที่แตกต่างกัน) MethodInterceptor
แต่ก็ยังจะช่วยให้การเรียกวิธีซุปเปอร์โดยใช้ไล่สูงขึ้นเป็นตัวอย่างเช่น นอกจากนี้ CGLIB FixedValue
สามารถปรับปรุงประสิทธิภาพการทำงานโดยเซพชั่นพิเศษเช่น ผมเคยเขียนบทสรุปของการไล่ที่แตกต่างกันสำหรับ CGLIB
ความแตกต่างด้านประสิทธิภาพ
ผู้รับมอบฉันทะ JDK InvocationHandler
จะดำเนินการค่อนข้างไร้เดียงสาที่มีเพียงการสกัดกั้นผู้มอบหมายงานหนึ่ง สิ่งนี้ต้องการวิธีการเสมือนที่ส่งไปยังการนำไปใช้งานซึ่งไม่สามารถ inline ได้ตลอดเวลา Cglib อนุญาตให้สร้างโค้ดไบต์พิเศษซึ่งบางครั้งสามารถปรับปรุงประสิทธิภาพได้ นี่คือการเปรียบเทียบบางส่วนสำหรับการใช้อินเทอร์เฟซกับวิธีการ 18 สตับ:
cglib JDK proxy
creation 804.000 (1.899) 973.650 (1.624)
invocation 0.002 (0.000) 0.005 (0.000)
เวลาถูกบันทึกไว้ในหน่วยนาโนวินาทีโดยมีค่าเบี่ยงเบนมาตรฐานเป็นเครื่องหมายวงเล็บ คุณสามารถค้นหารายละเอียดเพิ่มเติมเกี่ยวกับเกณฑ์มาตรฐานในบทช่วยสอนของ Byte Buddy ที่ Byte Buddy เป็นทางเลือกที่ทันสมัยกว่า cglib นอกจากนี้โปรดทราบว่า cglib ไม่อยู่ภายใต้การพัฒนาที่ใช้งานได้อีกต่อไป
พร็อกซี่แบบไดนามิก:ใช้งานแบบไดนามิกของอินเตอร์เฟซที่ใช้งานจริงโดยใช้JDK สะท้อน API
ตัวอย่าง: Spring ใช้พรอกซีแบบไดนามิกสำหรับการทำธุรกรรมดังนี้:
พร็อกซีที่สร้างขึ้นมาอยู่ด้านบนของ bean มันเพิ่มพฤติกรรมข้ามชาติในถั่ว ที่นี่พร็อกซีสร้างแบบไดนามิกตอนรันไทม์โดยใช้ JDK Reflection API
เมื่อแอปพลิเคชันหยุดทำงานพร็อกซีจะถูกทำลายและเราจะมีอินเตอร์เฟสและ bean ในระบบไฟล์เท่านั้น
ในตัวอย่างข้างต้นเรามีส่วนต่อประสาน แต่ในส่วนใหญ่ของการใช้อินเตอร์เฟซที่ไม่ดีที่สุด ดังนั้น bean ไม่ได้ใช้อินเตอร์เฟสในกรณีนั้นเราใช้การสืบทอด:
เพื่อสร้างพร็อกซี่ดังกล่าวสปริงใช้ไลบรารีบุคคลที่สามที่เรียกว่า CGLIB
CGLIB ( CบทกวีG eneration Lib Rary) จะถูกสร้างขึ้นบนASMนี้ส่วนใหญ่จะใช้สร้างถั่วขยายพร็อกซี่และเพิ่มพฤติกรรมถั่วในวิธีการพร็อกซี่
Spring AOP ใช้ JDK dynamic proxies หรือ CGLIB เพื่อสร้าง proxy สำหรับวัตถุเป้าหมายที่กำหนด (ต้องการพร็อกซี JDK แบบไดนามิกทุกครั้งที่คุณมีตัวเลือก)
หากวัตถุเป้าหมายที่จะใช้พร็อกซีนำไปใช้อย่างน้อยหนึ่งอินเตอร์เฟสจะใช้พร็อกซี JDK แบบไดนามิก อินเทอร์เฟซทั้งหมดที่ใช้งานตามประเภทเป้าหมายจะถูกนำเสนอ หากวัตถุเป้าหมายไม่ได้ใช้อินเตอร์เฟสใด ๆ ดังนั้นพร็อกซี CGLIB จะถูกสร้างขึ้น
หากคุณต้องการบังคับใช้การใช้ CGLIB proxying (ตัวอย่างเช่นเพื่อให้พร็อกซีทุกวิธีที่กำหนดไว้สำหรับวัตถุเป้าหมาย อย่างไรก็ตามมีบางประเด็นที่ต้องพิจารณา:
ไม่สามารถแนะนำวิธีการขั้นสุดท้ายได้เนื่องจากไม่สามารถ overriden ได้
คุณจะต้องใช้ CGLIB 2 ไบนารีบน classpath ของคุณในขณะที่พร็อกซีไดนามิกพร้อมใช้งานกับ JDK Spring จะเตือนคุณโดยอัตโนมัติเมื่อต้องการ CGLIB และไม่พบคลาสไลบรารี CGLIB ใน classpath
คอนสตรัคเตอร์ของวัตถุพร็อกซีของคุณจะถูกเรียกสองครั้ง นี่เป็นผลลัพธ์ตามธรรมชาติของโมเดลพร็อกซี CGLIB โดยคลาสย่อยจะถูกสร้างขึ้นสำหรับแต่ละวัตถุพร็อกซี สำหรับแต่ละอินสแตนซ์พร็อกซีจะสร้างวัตถุสองรายการ: วัตถุพร็อกซีจริงและอินสแตนซ์ของคลาสย่อยที่ใช้คำแนะนำ ลักษณะการทำงานนี้จะไม่แสดงเมื่อใช้พรอกซี JDK โดยปกติแล้วการเรียกตัวสร้างของชนิดพร็อกซีสองครั้งไม่ใช่ปัญหาเนื่องจากมักจะมีเพียงการมอบหมายที่เกิดขึ้นและไม่มีการใช้ตรรกะจริงในตัวสร้าง