แนวคิดของ AOP
AOP: การเขียนโปรแกรมที่มุ่งเน้นด้าน (การเขียนโปรแกรมแบบส่วนที่มุ่งเน้น), Wikipedia อธิบายว่า: แง่มุมเป็นกลไกโมดูลาร์ใหม่ที่ใช้เพื่ออธิบายข้อกังวลเกี่ยวกับภาคตัดขวางที่กระจัดกระจายในวัตถุคลาสหรือฟังก์ชั่น การแยกข้อกังวลแบบตัดขวางจากข้อกังวลเป็นแนวคิดหลักของการเขียนโปรแกรมแทนเจนต์ การแยกโฟกัสทำให้รหัสที่แก้ปัญหาโดเมนที่เฉพาะเจาะจงเป็นอิสระจากตรรกะทางธุรกิจ รหัสของตรรกะทางธุรกิจไม่มีการเรียกใช้รหัสสำหรับปัญหาโดเมนเฉพาะ ความสัมพันธ์ระหว่างตรรกะทางธุรกิจและปัญหาโดเมนที่เฉพาะเจาะจงนั้นถูกห่อหุ้มและบำรุงรักษาผ่านส่วนต่างๆดังนั้นการเปลี่ยนแปลงที่กระจัดกระจายไปทั่วแอปพลิเคชันสามารถจัดการได้ดี จากมุมมองของ AOP แอปพลิเคชันสามารถแบ่งออกเป็นข้อกังวลแบบตัดขวางและรหัสตรรกะทางธุรกิจ ในการพัฒนาที่เกิดขึ้นจริงข้อกังวลแบบตัดขวางเหล่านี้มักจะถูกฝังโดยตรงในรหัสตรรกะทางธุรกิจ การเขียนโปรแกรมแบบใบหน้าคือการแก้ปัญหาการแยกข้อกังวลแบบตัดขวางออกจากตรรกะทางธุรกิจ
วิธีการดำเนินการ:
สปริงใช้พร็อกซีไดนามิก JDK เป็นพร็อกซีของ AOP โดยค่าเริ่มต้น ข้อบกพร่องคือคลาสเป้าหมายจะต้องใช้อินเทอร์เฟซมิฉะนั้นไม่สามารถใช้พร็อกซีไดนามิก JDK หากคลาสเป็นคลาสแทนที่จะเป็นอินเทอร์เฟซสปริงจะใช้ CGLIB Proxy โดยค่าเริ่มต้น เกี่ยวกับความแตกต่างระหว่างสอง: JDK Dynamic Proxy ถูกนำมาใช้ผ่านกลไกการสะท้อนของ Java คลาสเป้าหมายจะต้องใช้อินเทอร์เฟซและ CGLIB ใช้พร็อกซีสำหรับคลาส หลักการของมันคือการสร้างคลาสย่อยแบบไดนามิกสำหรับคลาสเป้าหมายที่ระบุและแทนที่การปรับปรุงการใช้งานวิธีการ แต่เนื่องจากการสืบทอดถูกนำมาใช้คลาสที่ปรับเปลี่ยนขั้นสุดท้ายไม่สามารถ proxyed ได้
พร็อกซีไดนามิก JDK
JDK Dynamic Proxy สร้างไฟล์คลาสแบบไดนามิกของคลาสพร็อกซีตามอินเตอร์เฟสที่ใช้โดยคลาสเป้าหมายในระหว่างการดำเนินการโปรแกรม การใช้งานส่วนใหญ่เกี่ยวข้องกับสองคลาส:
อินเตอร์เฟส InvocationHandler: มันมีวิธี invoke(Object obj,Method method, Object[] args) สำหรับผู้ดำเนินการเพื่อให้การใช้งานลอจิกพร็อกซีที่สอดคล้องกัน การประมวลผลพิเศษบางอย่างสามารถทำได้ในการใช้งานจริงและพารามิเตอร์คือ
Object OBJ: คลาสเป้าหมายที่เป็นพร็อกซี
วิธีการ: วิธีการของคลาสเป้าหมายที่ต้องดำเนินการ
Object [] args: พารามิเตอร์ของวิธีเป้าหมาย
Proxy Class: จัดเตรียมวิธีการ newProxyInstance (ClassLoader loader, Class[] interfaces, InvocationHandler h) เพื่อให้ได้คลาสพร็อกซีแบบไดนามิก
รหัสตัวอย่าง:
คำสั่งอินเทอร์เฟซสาธารณะ {โมฆะสาธารณะ CreateOrder (); - Public Class OrderserViceImpl ใช้คำสั่ง {@Override โมฆะสาธารณะ createOrder () {System.out.println ("การสร้างคำสั่งซื้อ"); - คลาสสาธารณะ orderlogger {โมฆะสาธารณะ beforecreate order () {system.out.println ("ก่อนสร้างคำสั่งซื้อ"); } โมฆะสาธารณะ aftercreateOrder () {system.out.println ("หลังจากสร้างคำสั่งซื้อ"); - แพ็คเกจ com.sl.aop; นำเข้า java.lang.reflect.invocationhandler; นำเข้า java.lang.reflect.method; นำเข้า java.lang.reflect.proxy; การให้บริการระดับสาธารณะ orderlogger ส่วนตัว orderlogger; Public ServiceProxy (Object TargetClass, OrderLogger OrderLogger) {this.targetClass = TargetClass; this.orderlogger = orderlogger; } // รับพร็อกซีวัตถุสาธารณะ getDynamicProxy () {return proxy.newproxyinstance (targetClass.getClass (). getClassLoader (), // สร้างวัตถุพร็อกซีผ่าน classloader นี้ targetClass.getClass () getInterfaces () // // วิธีการเรียกพร็อกซีแบบไดนามิกเป็นวิธีการที่เกี่ยวข้องกับการเรียกร้องให้ใช้งานและในที่สุดก็ดำเนินการวิธีการจริงผ่านวิธีการเรียกใช้ของการเรียกใช้ hiscocationhandler นี้} // ใช้ลอจิกพร็อกซีตรรกะที่เกี่ยวข้อง @Override วัตถุสาธารณะเรียกใช้ (พร็อกซีวัตถุวิธีการ, วัตถุ [] args) Object result = method.invoke (targetclass, args); this.orderlogger.aftercreateorder (); ผลการกลับมา; -คลาสทดสอบ:
แพ็คเกจ com.sl.aop; นำเข้า org.junit.test; คลาสสาธารณะ aoptest {@Test โมฆะสาธารณะ testDynamicProxy () {orderServiceImpl serviceImpl = คำสั่งซื้อใหม่ (); orderlogger logger = new OrderLogger (); Service OrderService = (OrderService) ServiceProxy ใหม่ (ServiceImpl, Logger) .getDynamicProxy (); Service.createLore (); -ผลการทำงาน:
จริง ๆ แล้วฉันสับสนเล็กน้อยเมื่อมาถึงจุดนี้ Proxy.newProxyInstance() ส่งคืนอะไร? วิธีการเรียกใช้เรียกว่าที่ไหน? ลองดูที่ซอร์สโค้ด JDK: ดูว่ากระบวนการของพร็อกซี DK Dynamic เป็นอย่างไร:
โทร Proxy.newProxyInstance()->Proxy.getProxyClass0()->WeakCache.get() ตามฟังก์ชั่นภายในซอร์สโค้ดและค้นหาครั้งแรก
beatcache.class:
สาธารณะ v get (k key, พารามิเตอร์ p) {objects.requirenonnull (พารามิเตอร์); expungestaleentries (); CACHEY OBJECT = CACHEY.VALUEOF (คีย์, refqueue); // การติดตั้งค่าระดับที่ 2 อย่างเกียจคร้านสำหรับ CACHEKE โดยเฉพาะ CACHEY COMARRENTMAP <Object, ซัพพลายเออร์ <v>> valuesMap = map.get (cachekey); if (valuesMap == null) {concurrentMap <วัตถุ, ซัพพลายเออร์ <v>> oldValuesMap = map.putifabsent (cachekey, valuemap = ใหม่พร้อมกันพร้อมกัน <> ()); if (oldValuesMap! = null) {valueMap = oldValUESMAP; }} // สร้าง subkey และดึงซัพพลายเออร์ที่เป็นไปได้ <v> เก็บไว้โดย // subkey จาก valuemap object subkey = objects.requirenonnull (subkeyfactory.apply (คีย์, พารามิเตอร์)); ซัพพลายเออร์ <v> ซัพพลายเออร์ = valuemap.get (subkey); โรงงานโรงงาน = null; ในขณะที่ (จริง) {ถ้า (ซัพพลายเออร์! = null) {// ซัพพลายเออร์อาจเป็นโรงงานหรือ cachevalue <v> อินสแตนซ์ v value = supplier.get (); if (value! = null) {ค่าส่งคืน; }} // อื่น ๆ ไม่มีซัพพลายเออร์ในแคช // หรือซัพพลายเออร์ที่ส่งคืนค่า null (สามารถเป็น cachevalue ที่เคลียร์ // หรือโรงงานที่ไม่ประสบความสำเร็จในการติดตั้ง cachevalue) // สร้างโรงงานอย่างขี้เกียจถ้า (โรงงาน == null) {โรงงาน = โรงงานใหม่ } if (ซัพพลายเออร์ == null) {supplier = valuesMap.putifabsent (subkey, โรงงาน); if (ซัพพลายเออร์ == null) {// ติดตั้งผู้จัดหาโรงงานได้สำเร็จ = โรงงาน; } // else retry กับผู้ที่ชนะ} else {ถ้า (valuemap.replace (subkey, ซัพพลายเออร์, โรงงาน)) {// แทนที่ได้สำเร็จ // ล้าง cacheentry / โรงงานที่ไม่ประสบความสำเร็จ // กับผู้จัดหาโรงงาน = โรงงานของเรา; } else {// retry กับซัพพลายเออร์ซัพพลายเออร์ปัจจุบัน = valuemap.get (subkey); - คุณสามารถดูค่าคืนฟังก์ชั่น; และ V value = supplier.get(); อ่านต่อไปและพบว่าอาหารมื้อเย็น = โรงงานเป็นวัตถุ Factory.get() จริง ๆ
การซิงโครไนซ์สาธารณะ v get () {// serialize การเข้าถึง // ซัพพลายเออร์ตรวจสอบใหม่ <v> ซัพพลายเออร์ = valuesmap.get (subkey); ถ้า (ซัพพลายเออร์! = สิ่งนี้) {// มีบางอย่างเปลี่ยนไปในขณะที่เรากำลังรอ: // อาจเป็นได้ว่าเราถูกแทนที่ด้วย cachevalue // หรือถูกลบออกเนื่องจากความล้มเหลว -> // ส่งคืน null เป็นสัญญาณ beatcache.get () เพื่อลองใหม่ // } // อื่น ๆ ยังเรา (ซัพพลายเออร์ == สิ่งนี้) // สร้างค่าใหม่ v value = null; ลอง {value = objects.requirenonnull (valueFactory.apply (คีย์, พารามิเตอร์)); } ในที่สุด {ถ้า (value == null) {// ลบเราในค่าความล้มเหลว map.remove (subkey, this); }} // เส้นทางเดียวที่จะไปถึงที่นี่คือด้วยค่าที่ไม่ใช่ค่า NULL ASSERT! = NULL; // ห่อค่าด้วย cachevalue (beakreference) cachevalue <v> cachevalue = cachevalue ใหม่ <> (ค่า); // ลองแทนที่เราด้วย cachevalue (ควรประสบความสำเร็จเสมอ) ถ้า (valuemap.replace (subkey, this, cachevalue)) {// ใส่ใน Reversemap Reversemap.put (cachevalue, boolean.true); } else {โยน assertionError ใหม่ ("ไม่ควรไปถึงที่นี่"); } // แทนที่เราด้วย cachevalue ใหม่สำเร็จ -> ส่งคืนค่า // ห่อด้วยค่าคืนมัน; - ค่าส่งคืน; จากนั้นดูคำสั่งการกำหนดโดยตรง: value = Objects.requireNonNull(valueFactory.apply(key, parameter));
ValueFactory คืออะไร?
Public Weakcache (bifunction <k, p,?> subkeyfactory, bifunction <k, p, v> valuefactory) {this.subkeyfactory = objects.requirenonnull (subkeyfactory); this.valueFactory = objects.requirenonnull (valueFactory); } private static final finalcache <classloader, class <?> [], คลาส <? >> proxyclasscache = new beakcache <> (ใหม่ keyFactory (), proxyclassFactory ใหม่ ()); คุณสามารถรู้ได้ว่า ValueFactory เป็นวัตถุประเภท proxyclassfactory และดู ProxyClassFactory. Apply() โดยตรง ProxyClassFactory. Apply() วิธีการ
คลาสสาธารณะ <?> ใช้ (classloader loader, คลาส <?> [] อินเตอร์เฟส) {แผนที่ <คลาส <?>, boolean> interfaceset = new identityHashMap <> (interfaces.length); สำหรับ (คลาส <?> intf: อินเตอร์เฟส) { / * * ตรวจสอบว่าตัวโหลดคลาสจะแก้ไขชื่อของอินเตอร์เฟส * นี้ไปยังวัตถุคลาสเดียวกัน */ class <?> interfaceclass = null; ลอง {interfaceclass = class.forName (intf.getName (), false, loader); } catch (classnotFoundException e) {} ถ้า (interfaceclass! = intf) {โยน unleglArgumentException ใหม่ (intf + "ไม่สามารถมองเห็นได้จากคลาสรถโหลด"); } / * * ตรวจสอบว่าวัตถุคลาสจริงแสดงถึง * อินเทอร์เฟซ */ if (! interfaceclass.isinterface ()) {โยน unlegalargumentException ใหม่ (interfaceclass.getName () + "ไม่ใช่อินเตอร์เฟส"); } / * * ตรวจสอบว่าอินเทอร์เฟซนี้ไม่ซ้ำกัน */ if (interfaceset.put (interfaceclass, boolean.true)! = null) {โยน unlegalargumentException ใหม่ ("อินเทอร์เฟซซ้ำ:" + interfaceclass.getName ()); }} สตริง proxypkg = null; // แพ็คเกจเพื่อกำหนดคลาสพร็อกซีใน Int AccessFlags = modifier.public | modifier.final; / * * บันทึกแพ็คเกจของอินเทอร์เฟซพร็อกซีที่ไม่ใช่สาธารณะเพื่อให้คลาสพร็อกซี * จะถูกกำหนดไว้ในแพ็คเกจเดียวกัน ตรวจสอบว่า * อินเทอร์เฟซพร็อกซีที่ไม่ใช่สาธารณะทั้งหมดอยู่ในแพ็คเกจเดียวกัน */ สำหรับ (คลาส <?> intf: อินเตอร์เฟส) {int flags = intf.getModifiers (); if (! modifier.ispublic (แฟล็ก)) {accessFlags = modifier.final; ชื่อสตริง = intf.getName (); int n = name.lastindexof ('.'); สตริง pkg = ((n == -1)? "": name.substring (0, n + 1)); if (proxypkg == null) {proxypkg = pkg; } else if (! pkg.equals (proxypkg)) {โยน unlegalargumentException ใหม่ ("อินเทอร์เฟซที่ไม่ใช่แบบสาธารณะจากแพ็คเกจที่แตกต่างกัน"); }}}} if (proxypkg == null) {// ถ้าไม่มีอินเทอร์เฟซพร็อกซีที่ไม่ใช่สาธารณะใช้ com.sun.proxy แพ็คเกจ proxypkg = rechormutil.proxy_package + "."; } / * * เลือกชื่อสำหรับคลาสพร็อกซีเพื่อสร้าง */ long num = nextuniquenumber.getandincrement (); สตริง proxyname = proxypkg + proxyclassNamePrefix + num; / * * สร้างคลาสพร็อกซีที่ระบุ */ byte [] proxyclassfile = proxygenerator.generateproxyclass (proxyname, อินเตอร์เฟส, accessFlags); ลอง {return defeleclass0 (loader, proxyname, proxyclassfile, 0, proxyclassfile.length); } catch (classformaterror e) { / * * classformaterror ที่นี่หมายความว่า (ยกเว้นข้อผิดพลาดในรหัสการสร้างคลาสพร็อกซี *) มีบางแง่มุม * ที่ไม่ถูกต้องของอาร์กิวเมนต์ที่จัดทำให้กับการสร้างระดับพร็อกซี * (เช่นข้อ จำกัด ของเครื่องเสมือน * เกิน) */ โยน unlegalargumentException ใหม่ (e.toString ()); -วาดประเด็นสำคัญโดยตรง:
BYTE [] PROXYCLASSFILE = PROXYGENERATOR.GenerateProxyClass (Proxyname, อินเตอร์เฟส, AccessFlags); ส่งคืน defineclass0 (loader, proxyname, proxyclassfile, 0, proxyclassfile.length);
การเรียก ProxyGenerator.generateProxyClass ในที่สุดก็สร้างคลาสพร็อกซีแบบไดนามิก แต่ดูเหมือนว่าไม่มีที่ไหนพบว่าเรียกว่า Invoke; อ้างถึงบทความ CSDN: //www.vevb.com/article/118935.htm ลองส่งออกไบนารีไบนารีไบนารีที่สร้างขึ้นแบบไดนามิกในเครื่องและถอดรหัสเพื่อดูว่ามันคืออะไร รหัสทดสอบมีดังนี้:
Public Class aoptest {@Test โมฆะสาธารณะ testDynamicProxy () {orderSerViceImpl serviceImpl = คำสั่งซื้อใหม่ (); orderlogger logger = new OrderLogger (); Service OrderService = (OrderService) ServiceProxy ใหม่ (ServiceImpl, Logger) .getDynamicProxy (); Service.createLore (); // เอาท์พุทคลาสพร็อกซีแบบไดนามิก bytecode createProxyClassFile (); } โมฆะคงที่ส่วนตัว createProxyClassFile () {string name = "proxyObject"; ไบต์ [] data = proxygenerator.generateproxyclass (ชื่อ, คลาสใหม่ [] {orderservice.class}); fileOutputStream out = null; ลอง {out = ใหม่ fileOutputStream (ชื่อ+". class"); System.out.println ((ไฟล์ใหม่ ("hello")). getabsolutepath ()); out.write (ข้อมูล); } catch (filenotfoundException e) {e.printStackTrace (); } catch (ioexception e) {e.printstacktrace (); } ในที่สุด {ถ้า (null! = ออก) ลอง {out.close (); } catch (ioexception e) {e.printstacktrace (); -ใช้เครื่องมือ decompiler Java เพื่อถอดรหัสไฟล์คลาสไบนารีนี้:
พร็อกซีคลาสแบบไดนามิกเฉพาะ proxyobject.java:
นำเข้า com.sl.aop.orderservice; นำเข้า java.lang.reflect.invocationhandler นำเข้า java.lang.reflect.method; นำเข้า java.lang.reflect.proxy; นำเข้า java.lang วิธีการคงที่ส่วนตัว M2; วิธีการคงที่ส่วนตัว M3; วิธีการคงที่ส่วนตัว M0; proxyObject สาธารณะ (InvocationHandler paraminVocationHandler) {super (paraminVocationHandler); } สาธารณะบูลีนสุดท้ายเท่ากับ (วัตถุ paramobject) {ลอง {return ((บูลีน) this.h.invoke (นี่, m1, วัตถุใหม่ [] {paramobject})). booleanValue (); } catch (ข้อผิดพลาด | runtimeException localerror) {โยน localError; } catch (localthrowable localable) {โยน undeclaredthrowableexception ใหม่ (localthrowable); }} Public Final String ToString () {ลอง {return (string) this.h.invoke (นี่, m2, null); } catch (ข้อผิดพลาด | runtimeException localerror) {โยน localError; } catch (localthrowable localable) {โยน undeclaredthrowableexception ใหม่ (localthrowable); }} โมฆะสุดท้ายสาธารณะ createOrder () {ลอง {this.h.invoke (นี่, m3, null); กลับ; } catch (ข้อผิดพลาด | runtimeException localerror) {โยน localError; } catch (localthrowable localable) {โยน undeclaredthrowableexception ใหม่ (localthrowable); }} สาธารณะ int สุดท้าย hashCode () {ลอง {return ((จำนวนเต็ม) this.h.invoke (นี่, m0, null)). intvalue (); } catch (ข้อผิดพลาด | runtimeException localerror) {โยน localError; } catch (localthrowable localable) {โยน undeclaredthrowableexception ใหม่ (localthrowable); }} คงที่ {ลอง {m1 = class.forName ("java.lang.Object"). getMethod ("เท่ากับ", คลาสใหม่ [] {class.forName ("java.lang.Object")}); m2 = class.forname ("java.lang.Object"). getMethod ("toString", คลาสใหม่ [0]); m3 = class.forname ("com.sl.aop.orderservice"). getMethod ("createOrder", คลาสใหม่ [0]); m0 = class.forname ("java.lang.Object"). getMethod ("hashCode", คลาสใหม่ [0]); กลับ; } catch (nosuchmethodexception localnosuchmethodexception) {โยน nosuchmethoderror ใหม่ (localnosuchmethodexception.getMessage ()); } catch (classnotfoundexception localclassnotfoundexception) {โยน noclassdeffounderror ใหม่ (localclassnotfoundexception.getMessage ()); -ในที่สุดก็เห็นส่วนเกี่ยวกับการเรียก:
โมฆะสุดท้ายสาธารณะ createOrder () {ลอง {this.h.invoke (นี่, m3, null); กลับ; } catch (ข้อผิดพลาด | runtimeException localerror) {โยน localError; } catch (localthrowable localable) {โยน undeclaredthrowableexception ใหม่ (localthrowable); -ในความเป็นจริงคลาสพร็อกซีแบบไดนามิกสืบทอดมาจากพร็อกซีและดำเนินการอินเทอร์เฟซที่สืบทอดโดยคลาสเป้าหมาย วิธีการเรียกใช้ในเมธอด CreateRorder ซึ่งใช้การฝังตรรกะของส่วน ที่นี่เรายังตอบคำถามด้วยเหตุนี้คลาสเป้าหมายของพร็อกซีไดนามิก JDK จะต้องใช้อินเทอร์เฟซเพราะคลาสพร็อกซีนั้นมุ่งเป้าไปที่พร็อกซีอินเตอร์เฟสไม่ใช่คลาส คลาสพร็อกซีแบบไดนามิกสืบทอดตัวเองจากพร็อกซีและ Java ไม่อนุญาตให้มีการสืบทอดหลายครั้ง คลาสพร็อกซีแบบไดนามิกและคลาสเป้าหมายจะใช้อินเตอร์เฟสตามลำดับ คลาสพร็อกซีตระหนักถึงการโทรไปยังเมธอดคลาสเป้าหมายผ่าน InvocationHandler.invoke
พร็อกซีแบบไดนามิก CGLIB
CGLIB Proxy ใช้เฟรมเวิร์กการประมวลผลแบบไบต์เพื่อแปลงไบต์และสร้างคลาสใหม่และใช้เทคนิคการสกัดกั้นวิธีการในคลาสย่อยเพื่อสกัดกั้นวิธีการระดับพาเรนต์ทั้งหมดเพื่อใช้ตรรกะการตัดข้ามซึ่งมีประสิทธิภาพมากกว่า JDK Dynamic อย่างไรก็ตามเนื่องจากหลักการของ CGLIB คือการสร้างคลาสพร็อกซีย่อยแบบไดนามิกสำหรับคลาสเป้าหมายจึงไม่สามารถพร็อกซีสำหรับวิธีการที่ประกาศเป็นขั้นสุดท้าย การใช้งานส่วนใหญ่เกี่ยวข้องกับสองประเภท:
อินเตอร์เฟส MethodInterceptor: อินเทอร์เฟซนี้ให้วิธี intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) ส่วนใหญ่ใช้เพื่อสกัดกั้นการเรียกใช้วิธีการคลาสเป้าหมาย
Object Arg0,: คลาสเป้าหมายที่กำลังเป็นพร็อกซี
วิธีการ Arg1, วิธีการมอบหมาย
Object [] arg2, พารามิเตอร์วิธีการ
MethodProxy Arg3: MethodProxy Object of Proxy Method
คลาสเพิ่มขึ้น: ใช้เพื่อสร้างคลาสพร็อกซี
ตัวอย่าง:
ใช้อินเทอร์เฟซ MethodInterceptor เมื่อคลาสพร็อกซีเรียกวิธีการ CGLIB จะโทรกลับวิธีการสกัดกั้นอินเทอร์เฟซของ MethodInterceptor ซึ่งจะทอตรรกะพื้นผิว
แพ็คเกจ com.sl.aop; นำเข้า java.lang.reflect.method; นำเข้า org.springframework.cglib.proxy.enhancer; นำเข้า org.springframework.cglib.proxy.methodinterceptor; นำเข้า MethodInterceptor {วัตถุส่วนตัว TargetClass; orderlogger ส่วนตัว orderlogger; Public CGLIBSERVICEPROXY (Object TargetClass, OrderLogger OrderLogger) {this.targetClass = TargetClass; this.orderlogger = orderlogger; } / *** สร้างวัตถุพร็อกซี** / วัตถุสาธารณะ getInstance () {enhancer enhancer = new enhancer (); // ตั้งค่าคลาสเป้าหมาย (คลาสที่ต้องเป็น proxyed) enhancer.setsuperclass (this.targetClass.getClass ()); // วิธีการโทรกลับ Method.setCallback (นี่); // สร้างพร็อกซีออบเจ็กต์ return enhance.create (); } / *** สกัดกั้นวิธีการคลาสเป้าหมายทั้งหมด** / @Override การสกัดกั้นวัตถุสาธารณะ (Object Arg0, Method arg1, Object [] arg2, methodproxy arg3) โยน {orderlogger.beforecreate order (); Object O1 = Arg3.invokesuper (arg0, arg2); orderlogger.aftercreateorder (); กลับ O1; -วิธีทดสอบ:
โมฆะสาธารณะ TestDynamicProxy () {System.SetProperty (DebuggingClassWriter.debug_location_property, "d: // class"); orderServiceImpl serviceImpl = คำสั่งใหม่ orderserviceimpl (); orderlogger logger = new OrderLogger (); CGLIBSERVICEPROXY PROXY = ใหม่ CGLIBSERVICEPROXY (ServiceImpl, Logger); // สร้างคลาสพร็อกซีโดยการสร้าง subclasses orderServiceImpl proxyimp = (orderserviceimpl) proxy.getInstance (); Proxyimp.createDorder (); -ผลลัพธ์:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://class"); เอาท์พุท CGLIB Dynamic Proxy Class ไปยังไดเรกทอรีที่ระบุถอดรหัสและตรวจสอบใบหน้าที่แท้จริงของพร็อกซีคลาส:
แพ็คเกจ com.sl.aop; นำเข้า com.sl.aop.orderserviceimpl; นำเข้า java.lang.reflect.method; นำเข้า org.springframework.cglib.core.reflectutils; นำเข้า org.springframework.cglib.core.signature; org.springframework.cglib.proxy.factory; นำเข้า org.springframework.cglib.proxy.methodinterceptor; นำเข้า org.springframework.cglib.proxy.methodproxy; บูลีน cglib $ bound; วัตถุคงที่สาธารณะ cglib $ stactory_data; private static final threadlocal cglib $ thread_callbacks; การโทรกลับครั้งสุดท้ายแบบคงที่ส่วนตัว [] cglib $ static_callbacks; MethodInterceptor ส่วนตัว cglib $ callback_0; วัตถุคงที่ส่วนตัว cglib $ callback_filter; วิธีสุดท้ายคงที่ส่วนตัว cglib $ createOrder $ 0 $ วิธี; Private Static Final Method Proxy CGLIB $ createOrder $ 0 $ พร็อกซี; วัตถุสุดท้ายคงที่ส่วนตัว [] cglib $ emportargs; วิธีสุดท้ายแบบคงที่ cglib $ เท่ากับ $ 1 $ วิธี; Methodproxy cglib สุดท้ายคงที่ $ เท่ากับ $ 1 $ พร็อกซี; วิธีสุดท้ายคงที่ส่วนตัว cglib $ toString $ 2 $ วิธี; Methodproxy cglib สุดท้ายคงที่ $ 2 $ พร็อกซี; วิธีสุดท้ายคงที่ส่วนตัว cglib $ hashcode $ 3 $ วิธี; Methodproxy cglib $ hashcode $ 3 $ proxy; Private Static Final MethodCglib $ clone $ 4 $ วิธี; Methodproxy cglib สุดท้ายคงที่ $ 4 $ proxy; โมฆะคงที่ cglib $ statichook1 () {cglib $ thread_callbacks = new ThreadLocal (); cglib $ emportargs = วัตถุใหม่ [0]; คลาส var0 = class.forname ("com.sl.aop.orderserviceimpl $$ enhancerbycglib $$ 17779aa4"); คลาส VAR1; วิธีการ [] var10000 = rechlectutils.findmethods (สตริงใหม่ [] {"เท่ากับ", "(ljava/lang/object;) z", "toString", "() ljava/lang/string;", "hashcode", "() ฉัน", "Clone" class.forName ("java.lang.Object")). getDeclaredMethods ()); cglib $ เท่ากับ $ 1 $ method = var10000 [0]; cglib $ เท่ากับ $ 1 $ proxy = methodProxy.create (var1, var0, "(ljava/lang/object;) z", "equals", "cglib $ เท่ากับ $ 1"); cglib $ toString $ 2 $ method = var10000 [1]; cglib $ toString $ 2 $ proxy = methodProxy.create (var1, var0, "() ljava/lang/string;", "toString", "cglib $ toString $ 2"); cglib $ hashcode $ 3 $ method = var10000 [2]; cglib $ hashcode $ 3 $ proxy = methodproxy.create (var1, var0, "() i", "hashcode", "cglib $ hashcode $ 3"); cglib $ clone $ 4 $ method = var10000 [3]; cglib $ clone $ 4 $ proxy = methodproxy.create (var1, var0, "() ljava/lang/object;", "clone", "cglib $ clone $ 4"); cglib $ createOrder $ 0 $ method = rechlemutils.findMethods (สตริงใหม่ [] {"createOrder", "() v"}, (var1 = class.forname ("com.sl.aop.orderserviceimpl")). getDeclaredMethods () [0]; cglib $ createOrder $ 0 $ proxy = methodProxy.create (var1, var0, "() v", "createOrder", "cglib $ createOrder $ 0"); } สุดท้ายเป็นโมฆะ cglib $ createOrder $ 0 () {super.createRorder (); } โมฆะสุดท้ายสาธารณะ createOrder () {MethodInterceptor var10000 = this.cglib $ callback_0; if (this.cglib $ callback_0 == null) {cglib $ bind_callbacks (นี่); var10000 = this.cglib $ callback_0; } if (var10000! = null) {var10000.intercept (นี่, cglib $ createOrder $ 0 $ method, cglib $ emportargs, cglib $ createOrder $ 0 $ proxy); } else {super.createorder (); }} บูลีนสุดท้าย cglib $ เท่ากับ $ 1 (Object var1) {return super.equals (var1); } สาธารณะบูลีนสุดท้ายเท่ากับ (object var1) {methodInterceptor var10000 = this.cglib $ callback_0; if (this.cglib $ callback_0 == null) {cglib $ bind_callbacks (นี่); var10000 = this.cglib $ callback_0; } if (var10000! = null) {object var2 = var10000.intercept (นี่, cglib $ เท่ากับ $ 1 $ วิธี, วัตถุใหม่ [] {var1}, cglib $ เท่ากับ $ 1 $ proxy); return var2 == null? talse: ((บูลีน) var2) .BooleanValue (); } else {return super.equals (var1); }} สตริงสุดท้าย cglib $ toString $ 2 () {return super.toString (); } สาธารณะสตริงสุดท้าย toString () {methodInterceptor var10000 = this.cglib $ callback_0; if (this.cglib $ callback_0 == null) {cglib $ bind_callbacks (นี่); var10000 = this.cglib $ callback_0; } return var10000! = null? (String) var10000.intercept (นี่, cglib $ toString $ 2 $ วิธี, cglib $ emportargs, cglib $ toString $ 2 $ proxy): super.tostring (); } สุดท้าย int cglib $ hashCode $ 3 () {return super.hashCode (); } สาธารณะ int สุดท้าย hashCode () {methodInterceptor var10000 = this.cglib $ callback_0; if (this.cglib $ callback_0 == null) {cglib $ bind_callbacks (นี่); var10000 = this.cglib $ callback_0; } if (var10000! = null) {object var1 = var10000.intercept (นี่, cglib $ hashcode $ 3 $ วิธี, cglib $ emportargs, cglib $ hashcode $ 3 $ proxy); return var1 == null? 0: ((หมายเลข) var1) .intvalue (); } else {return super.hashCode (); }} วัตถุสุดท้าย cglib $ clone $ 4 () พ่น clonenotsupportedexception {return super.clone (); } การป้องกันวัตถุสุดท้ายโคลน () พ่น clonenotsupportedException {methodInterceptor var10000 = this.cglib $ callback_0; if (this.cglib $ callback_0 == null) {cglib $ bind_callbacks (นี่); var10000 = this.cglib $ callback_0; } return var10000! = null? var10000.intercept (นี่, cglib $ clone $ 4 $ วิธี, cglib $ emportargs, cglib $ clone $ 4 $ พร็อกซี): super.clone (); } public Static MethodProxy CGLIB $ findMetHodProxy (ลายเซ็น VAR0) {String VAR10000 = VAR0.TOSTRING (); สวิตช์ (var10000.hashcode ()) {case -2138148221: if (var10000.equals ("createOrder () v")) {return cGlib $ createOrder $ 0 $ proxy; } หยุดพัก; กรณี -508378822: ถ้า (var10000.equals ("clone () ljava/lang/object;")) {return cglib $ clone $ 4 $ พร็อกซี; } หยุดพัก; กรณี 1826985398: ถ้า (var10000.equals ("เท่ากับ (ljava/lang/object;) z")) {return cglib $ เท่ากับ $ 1 $ พร็อกซี; } หยุดพัก; กรณี 1913648695: ถ้า (var10000.equals ("toString () ljava/lang/string;"))) {return cglib $ toString $ 2 $ พร็อกซี; } หยุดพัก; กรณี 1984935277: ถ้า (var10000.equals ("hashCode () i")) {return cglib $ hashCode $ 3 $ พร็อกซี; }} return null; } Public OrderServiceImpl $$ enhancerbycglib $$ 17779aa4 () {cglib $ bind_callbacks (นี่); } โมฆะคงที่สาธารณะ cglib $ set_thread_callbacks (callback [] var0) {cglib $ thread_callbacks.set (var0); } โมฆะคงที่สาธารณะ cglib $ set_static_callbacks (การโทรกลับ [] var0) {cglib $ static_callbacks = var0; } ส่วนตัวคงที่เป็นโมฆะสุดท้าย cglib $ bind_callbacks (Object var0) {orderserviceimpl $$ enhancerbycglib $$ 17779aa4 var1 = (orderserviceimpl $$ enhancerbycglib $$ 17779aa4) var0; if (! var1.cglib $ bound) {var1.cglib $ bound = true; Object var10000 = cglib $ thread_callbacks.get (); if (var10000 == null) {var10000 = cglib $ static_callbacks; if (cglib $ static_callbacks == null) {return; }} var1.cglib $ callback_0 = (methodInterceptor) ((callback []) var10000) [0]; }} วัตถุสาธารณะ newInstance (การโทรกลับ [] var1) {cglib $ set_thread_callbacks (var1); OrderServiceImpl $$ EnhancerByCglib $$ 17779AA4 VAR10000 = คำสั่งซื้อใหม่ $$ ENHANCERBYCGLIB $$ 17779AA4 (); cglib $ set_thread_callbacks ((โทรกลับ []) null); ส่งคืน VAR10000; } วัตถุสาธารณะ newInstance (callback var1) {cglib $ set_thread_callbacks (การโทรกลับใหม่ [] {var1}); OrderServiceImpl $$ EnhancerByCglib $$ 17779AA4 VAR10000 = คำสั่งซื้อใหม่ $$ ENHANCERBYCGLIB $$ 17779AA4 (); cglib $ set_thread_callbacks ((โทรกลับ []) null); ส่งคืน VAR10000; } วัตถุสาธารณะ newInstance (คลาส [] var1, object [] var2, callback [] var3) {cglib $ set_thread_callbacks (var3); OrderServiceImpl $$ EnhancerByCglib $$ 17779AA4 VAR10000 = คำสั่งซื้อใหม่ $$ ENHANCERBYCGLIB $$ 17779AA4; สวิตช์ (var1.length) {กรณี 0: var10000. <init> (); cglib $ set_thread_callbacks ((โทรกลับ []) null); ส่งคืน VAR10000; ค่าเริ่มต้น: โยน unlegalargumentException ใหม่ ("ไม่พบตัวสร้าง"); }} การโทรกลับสาธารณะ getCallback (int var1) {cglib $ bind_callbacks (นี่); MethodInterceptor VAR10000; สวิตช์ (var1) {กรณี 0: var10000 = this.cglib $ callback_0; หยุดพัก; ค่าเริ่มต้น: var10000 = null; } return var10000; } โมฆะสาธารณะ setCallback (int var1, callback var2) {switch (var1) {กรณี 0: this.cglib $ callback_0 = (methodInterceptor) var2; ค่าเริ่มต้น:}} การโทรกลับสาธารณะ [] getCallbacks () {cglib $ bind_callbacks (นี่); ส่งคืนการโทรกลับใหม่ [] {this.cglib $ callback_0}; } โมฆะสาธารณะ setCallbacks (การโทรกลับ [] var1) {this.cglib $ callback_0 = (methodInterceptor) var1 [0]; } คงที่ {cglib $ statichook1 (); -ในรหัสข้างต้นคุณจะเห็นว่าพร็อกซีคลาส OrderServiceImpl $$ enhancerbycglib $$ 17779AA4 ได้รับมรดกตามคำสั่งคลาสเป้าหมายและดำเนินการโรงงานอินเตอร์เฟส ในคลาสพร็อกซีมีสองวิธี cglib $ createOrder $ 0 และ createOrder ถูกสร้างขึ้น:
วิธี cglib $ createOrder $ 0 เรียกโดยตรงกับ supper.createOrder ของคลาสเป้าหมาย
เมธอด CreateOrder นับก่อนนับว่าการเรียกกลับของอินเตอร์เฟส MethodInterceptor ถูกนำไปใช้หรือไม่ หากมีอยู่วิธีการสกัดกั้นอินเตอร์เฟสเมดิเมนต์อินเตอร์เฟสจะเรียกว่า ตามการใช้งานก่อนหน้านี้การเรียกใช้วิธีการเป้าหมายจะถูกนำมาใช้ Object o1 = arg3.invokeSuper(arg0, arg2) ถูกนำมาใช้ InvokeSuper เป็นวิธี CGLIB$createOrder$0() ของคลาสพร็อกซีที่เรียกโดยตรงและในที่สุดคลาสเป้าหมายก็ถูกเรียกว่า
การเปรียบเทียบตัวแทนสองตัว
JDK Dynamic Proxy:
คลาสพร็อกซีและคลาสผู้แทนใช้อินเทอร์เฟซเดียวกัน ส่วนใหญ่ใช้ raveCocationHandler ผ่านคลาสพร็อกซีและเขียนวิธีการเรียกใช้เพื่อดำเนินการพร็อกซิงแบบไดนามิก วิธีการจะได้รับการปรับปรุงในวิธีการเรียกใช้ ข้อดีของวิธีการ: ไม่จำเป็นต้องใช้อินเทอร์เฟซรหัสฮาร์ดและอัตราการใช้ซ้ำรหัสสูง ข้อเสีย: เฉพาะคลาสตัวแทนที่สามารถนำไปใช้งานได้โดยอินเทอร์เฟซ
CGLIB Dynamic Proxy:
คลาสพร็อกซีใช้คลาสตัวแทนเป็นคลาสหลักและสร้างสองวิธีสำหรับวิธีการที่ไม่ใช่ผู้เข้าร่วมการแข่งขันในนั้น หนึ่งคือวิธีเดียวกับลายเซ็นวิธีการมอบหมายซึ่งจะเรียกวิธีการมอบหมายผ่าน Super ในวิธีการ; อีกวิธีหนึ่งเป็นวิธีที่ไม่ซ้ำกันกับคลาสพร็อกซี ในเมธอดพร็อกซีมันจะตรวจสอบว่ามีวัตถุที่ใช้อินเตอร์เฟส MethodInterceptor หรือไม่ หากมีอยู่วิธีการสกัดกั้นจะถูกเรียกให้พร็อกซีวิธีการมอบหมาย ข้อดี: สามารถปรับปรุงการทำงานของคลาสหรืออินเทอร์เฟซที่รันไทม์และคลาสผู้แทนไม่จำเป็นต้องใช้อินเทอร์เฟซ ข้อเสีย: มันไม่สามารถส่งมอบคลาสสุดท้ายและวิธีสุดท้าย
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com