مفتاح تنفيذ الوكيل الديناميكي في Java هو هذين الأمرين: الوكيل و InvocationHandler. دعنا نبدأ بطريقة الاستدعاء في واجهة InvocationHandler وشرح بإيجاز كيف تنفذ Java الوكيل الديناميكي.
أولاً ، الشكل الكامل لطريقة الاستدعاء هو كما يلي:
الكائن العام استدعاء (وكيل الكائن ، طريقة الطريقة ، الكائن [] args) رمي {method.invoke (obj ، args) ؛ return null ؛}أولاً ، دعنا نخمن أن الطريقة هي الطريقة التي تسمى ، أي الطريقة التي يجب تنفيذها ؛ ARGS هي معلمة الطريقة ؛ وكيل ، ما هي هذه المعلمة؟ التنفيذ أعلاه لطريقة Invoke () هو شكل قياسي نسبيًا. نرى أنه لا يتم استخدام معلمات الوكيل هنا. تحقق من وصف الوكيل في وثائق JDK ، على النحو التالي:
سيتم إرسال استدعاء طريقة على مثيل الوكيل من خلال أحد واجهات الوكيل الخاصة به إلى طريقة استدعاء معالج استدعاء المثيل ، تمرير مثيل الوكيل ، java.lang.reflect.method كائن تحديد الطريقة التي تم استدعاؤها ، ومجموعة من الكائن النوع الذي يحتوي على الحجج.
من هذا يمكننا أن نعرف أن التخمين أعلاه صحيح ، ونعلم أيضًا أن المعلمة الوكيل هي مثيل لفئة الوكيل.
لراحة التفسير ، إليك مثال بسيط لتنفيذ الوكيل الديناميكي.
// الدور التجريدي (الوكيل الديناميكي يمكن فقط واجهة الوكيل) موضوع الواجهة العامة {public void request () ؛ }. }}
// تنفيذ InvocationHandler Class Class Public DynamicsUbject تنفذ invocationHandler {private object obj ؛ // هذه هي الفائدة من الوكيل الديناميكي. الكائن المغلف من نوع الكائن ويقبل الكائنات من أي نوع DynamicSubject () {} DynamicSubject (Object OBJ) {this.Obj = obj ؛ }. method.invoke (obj ، args) ؛ System.out.println ("بعد الاتصال" + طريقة) ؛ العودة لاغية. }} // العميل: قم بإنشاء مثيل وكيل واستدعاء طريقة request () client client {public static void main (string [] args) رمي {// todo method method tuto method struct rs = new RealSUBJECT () الفئة <؟> cls = rs.getClass () ؛ // ما يلي هو جيل لمرة واحدة من موضوع الوكيل = (الموضوع) proxy.newproxyinstance (cls.getClassLoader () ، cls.getinterfaces () ، ds) ؛ // هنا يمكنك إثبات أن الموضوع هو مثيل للوكيل عن طريق تشغيل النتائج. يقوم هذا المثيل بتنفيذ نظام واجهة الموضوع. // هنا يمكنك أن ترى أن فئة فئة الموضوع هي $ proxy0. يرث فئة $ proxy0 الوكيل ويتخذ نظام واجهة الموضوع. System.out.print ("الخصائص في الموضوع هي:") ؛ الحقل [] الحقل = الموضوع. لـ (الحقل f: الحقل) {system.out.print (f.getName ()+"،") ؛ } الأساليب في system.out.print ("/n"+"الموضوع هي:") ؛ method [] method = thision.getClass (). getDeclaredMethods () ؛ لـ (method m: method) {system.out.print (m.getName ()+"،") ؛ } الفئة الأصل من system.out.println ("/n"+"الموضوع هو:"+thision.getClass (). getSuperClass ()) ؛ system.out.print ("/n"+"موضوع ينفذ الواجهة:") ؛ الفئة <؟> [] واجهات = موضوع. getClass (). getInterFaces () ؛ لـ (class <؟> i: interfaces) {system.out.print (i.getName ()+"،") ؛ } system.out.println ("/n/n"+"run regude هي:") ؛ الموضوع. request () ؛ }} نتيجة العملية على النحو التالي: تم حذف اسم الحزمة هنا ، *** بدلاً من ذلك
حقيقي
الفصل الدراسي للموضوع هو: class $ proxy0
الخصائص في الموضوع هي: M1 ، M3 ، M0 ، M2 ،
الأساليب في الموضوع هي: طلب ، hashcode ، يساوي ، tostring ،
فئة الأصل للموضوع هي: class java.lang.reflect.proxy
الواجهة التي تنفذها الموضوع هي: cn.edu.ustc.dynamicproxy.subject ،
نتيجة العملية هي:
قبل استدعاء الفراغ التجريدي العام ***. الموضوع. request ()
من موضوع حقيقي.
بعد استدعاء الفراغ التجريدي العام ***. الموضوع. request ()
ملاحظة: المعلومات المتعلقة بهذه النتيجة مهمة للغاية ، على الأقل بالنسبة لي. لأن السبب الجذري لدوارتي في الوكيل الديناميكي هو أنني أسيء فهم الموضوع أعلاه. request () ، على الأقل كنت مرتبكًا من السطح ولم أجد العلاقة بين الموضوع والوكيل. لقد كنت مرتبكًا ذات مرة حول كيفية اتصال آخر طلب استدعاء () إلى Invoke () ، وكيف عرفت أن الطلب موجود. في الواقع ، يمكن لـ True and Class $ proxy0 أعلاه حل العديد من الأسئلة ، والاقتران مع الكود المصدري لـ $ proxy0 الذي سيتم ذكره أدناه ، يمكنه حل شكوك الوكيل الديناميكي تمامًا.
من الكود والنتائج أعلاه ، يمكننا أن نرى أننا لم نسمي طريقة Invoke () كما هو موضح ، لكن هذه الطريقة قد تم تنفيذها. دعنا نحلل العملية بأكملها أدناه:
انطلاقًا من الرمز في العميل ، يمكنك استخدام طريقة NewProxyInstance كقائد. دعونا أولاً نلقي نظرة على الكود المصدري لطريقة NewProxyInstance في فئة الوكيل:
كائن ثابت عام NewProxyInstance (loader classloader ، فئة <؟> [] واجهات ، invocationHandler H) يلقي alfictalargumentException {if (h == null) {رمي nullpointerxception () ؛ } / * * ابحث عن أو إنشاء فئة الوكيل المصممة. */ class cl = getProxyclass (loader ، interfaces) ؛ / * * استدعاء مُنشئها مع معالج الاحتجاج المصمم. * / TREE { / * * يحتوي رمز مصدر الوكيل على التعريف التالي: * الفئة الثابتة النهائية الخاصة [] ConstructorParams = {invocationHandler.Class} ؛ * CONS هي طريقة المنشئ مع المعلمات الرسمية لنوع InvocationHandler*/ Constructor Cons = cl.getConstructor (constructorms) ؛ return (Object) cons.newinstance (كائن جديد [] {h}) ؛ } catch (nosuchmethodexception e) {رمي internalerror جديد (e.toString ()) ؛ } catch (alfictAccessException e) {throw new internalerror (e.ToString ()) ؛ } catch (instantiationException e) {رمي internalerror جديد (e.toString ()) ؛ } catch (invocationTargetException e) {رمي internalerror جديد (e.toString ()) ؛ }} proxy.newproxyinstance (loader classloader ، فئة <؟> [] واجهات ، invocationHandler H) هل الأشياء التالية.
(1) استدعاء الطريقة getProxyclass (loader ، واجهات) استنادًا إلى المعلمات المحمولة والواجهات ، وإنشاء Proxy Class $ proxy0. $ proxy0 ، وتنفيذ واجهات الواجهات ، ويرث فئة الوكيل.
(2) instantiate $ proxy0 وتمرير DynamicSubject في المُنشئ ، ثم يستدعي $ proxy0 مُنشئ وكيل الفئة الأصل ويعين قيمة إلى H ، على النحو التالي:
proxy class {invocationHandler H = null ؛ بروكسي محمي (invocationHandler H) {this.h = h ؛ } ...}دعونا نلقي نظرة على الكود المصدري الذي يرث الوكيل $ proxy0 من الوكيل:
Public Class $ proxy0 يمتد أدوات الوكيل الموضوع {الطريقة الثابتة الخاصة M1 ؛ الطريقة الثابتة الخاصة M0 ؛ الطريقة الثابتة الخاصة M3 ؛ الطريقة الثابتة الخاصة M2 ؛ ثابت {try {m1 = class.forname ("java.lang.object"). getMethod ("equals" ، class [] {class.forname ("java.lang.object")}) ؛ m0 = class.forname ("java.lang.object"). getMethod ("hashcode" ، فئة جديدة [0]) ؛ m3 = class.forname ("***. RealSubject"). getMethod ("request" ، فئة جديدة [0]) ؛ m2 = class.forname ("java.lang.object"). getMethod ("toString" ، فئة جديدة [0]) ؛ } catch (nosuchmethodexception nosuchmethodexception) {رمي nosuchmethoderror جديد (nosuchmethodexception.getMessage ()) ؛ } catch (classNotFoundException classNotFoundException) {رمي noclassDeffounderror (classnotfoundException.getMessage ()) ؛ }} // static public $ proxy0 (invocationHandler invocationHandler) {super (invocationHandler) ؛ } Override Public Final Boolean Equals (Object obj) {try {return ((boolean) super.h.invoke (this ، m1 ، comple new [] {obj})) .booleanvalue () ؛ } catch (throwable reflable) {رمي جديد غير محدد غير قابل للاعتداء (رمي) ؛ }} Override public final int hashcode () {try {return ((integer) super.h.invoke (this ، m0 ، null)). Intvalue () ؛ } catch (throwable reflable) {رمي جديد غير محدد غير قابل للاعتداء (رمي) ؛ }} طلب void النهائي العام () {try {super.h.invoke (this ، m3 ، null) ؛ يعود؛ } catch (error e) {} catch (throwable remable) {refl etre undeclaredthrowableexception (throwable) ؛ }} Override Public Final String ToString () {try {return (string) super.h.invoke (this ، m2 ، null) ؛ } catch (throwable reflable) {رمي جديد غير محدد غير قابل للاعتداء (رمي) ؛ }}}ثم قم بإلقاء مثيل $ proxy0 الناتج في موضوع ما وتعيين المرجع للموضوع. عند تنفيذ طريقة الموضوع. request () ، يتم استدعاء طريقة الطلب () في فئة $ proxy0 ، ويتم استدعاء طريقة Invoke () لـ H في وكيل الفئة الأصل. وهذا هو ، invocationHandler.invoke ().
PS: 1. شيء واحد يجب ملاحظته هو أن طريقة getProxyclass في فئة الوكيل إرجاع فئة الوكيل. والسبب في ذلك هو أنني ارتكبت خطأً منخفضًا في البداية ، معتقدًا أن العائد هو "فئة فئة من فئة الوكيل"-! يوصى بإلقاء نظرة على الكود المصدري لـ getProxyclass ، وهو طويل جدًا =. =
2. من الكود المصدري لـ $ proxy0 ، يمكن ملاحظة أن فئة الوكيل الديناميكية ليست فقط الوكلاء في الواجهة المحددة في العرض ، ولكن أيضًا الوكلاء الأساليب الثلاثة الموروثة للمتساويين () ، Hashcode () ، و toString () في كائن الفئة الجذر لـ Java ، والطرق الثلاث فقط.
س: حتى الآن ، لا يزال هناك سؤال. المعلمة الأولى في طريقة Invoke هي مثيل للوكالة (لدقة ، يتم استخدام مثيل $ proxy0 أخيرًا) ، ولكن ما هو الفائدة؟ أو كيف تظهر الوظيفة في البرنامج؟
ج: من مستواي الحالي ، فإن المعلمة الوكيل هذه ليس لها أي تأثير. في آلية الوكيل الديناميكي بأكمله ، لا يتم استخدام المعلمة الوكيل لطريقة الاستدعاء في InvocationHandler. المعلمة التي تم تمريرها هي في الواقع مثيل لفئة الوكيل. أعتقد أنه قد يكون السماح للمبرمجين باستخدام التفكير في طريقة الاستدعاء للحصول على بعض المعلومات حول فئة الوكيل.
لخص
ما سبق هو المحتوى الكامل لهذه المقالة حول طريقة Invoke () في InvocationHandler. آمل أن يكون ذلك مفيدًا للجميع. يمكن للأصدقاء المهتمين الاستمرار في الرجوع إلى هذا الموقع:
شرح مفصل للوكيل الثابت الربيعي ورمز الوكيل الديناميكي
مثال على طريقة حقن التبعية الإطار الربيعي
تنفيذ برمجة Java لـ SpringMVC مثال تسجيل الدخول البسيط
إذا كانت هناك أي أوجه قصور ، فيرجى ترك رسالة لإشارةها.