1. تعريف الوكيل
تزويد كائن بكائن وكيل ، ويتحكم كائن الوكيل في الوصول إلى الكائن الأصلي ، أي أن العميل لا يعالج مباشرة الكائن الأصلي ، ولكنه يعالج الكائن الأصلي بشكل غير مباشر من خلال الكائن الوكيل.
مثال على نمط الوكيل الشهير هو العد المرجعي: عندما تكون هناك حاجة إلى نسخ متعددة من كائن معقد ، يمكن دمج نمط الوكيل مع وضع التعريف لتقليل كمية الذاكرة. تتمثل النهج النموذجي في إنشاء كائن معقد ووكيل متعدد ، كل وكيل يشير إلى الكائن الأصلي. سيتم إرسال العمليات التي تعمل على الوكيل إلى الكائن الأصلي. بمجرد عدم وجود جميع العوامل ، تتم إزالة الكائنات المعقدة.
من السهل فهم نموذج الوكيل ، ولكن في الواقع هناك نموذج وكيل في الحياة:
يمكننا شراء تذاكر القطار في محطة القطار ، ولكن يمكننا أيضًا شرائها في مكتب مبيعات تذاكر القطار. مكتب مبيعات تذاكر القطار هنا هو وكيل شراء التذاكر في محطة القطار. وهذا هو ، نقوم بإصدار طلب شراء التذاكر في منفذ المبيعات. سيرسل منفذ المبيعات الطلب إلى محطة القطار ، وسوف ترسل محطة القطار الاستجابة الناجحة للشراء إلى منفذ المبيعات ، وسيخبرك منفذ المبيعات مرة أخرى.
ومع ذلك ، لا يمكن شراء التذاكر إلا من منفذ المبيعات ، ولكن ليس المبالغ المستردة ، في حين يمكن شراء التذاكر في محطة القطار ، وبالتالي فإن العمليات التي يدعمها الوكيل قد تختلف عن تلك الموجودة في الكائن المتكامل.
اسمحوا لي أن أقدم لك مثالًا آخر ستواجهه عند كتابة برنامج:
إذا كان هناك مشروع موجود (ليس لديك رمز مصدر ، فيمكنك الاتصال به فقط) يمكنه استدعاء int compute (سلسلة exp1) لتنفيذ حساب تعبير اللاحقة. إذا كنت ترغب في استخدام هذا المشروع لتنفيذ حساب تعبير Infix ، فيمكنك كتابة فئة وكيل وتحديد حساب (سلسلة Exp2). معلمة Exp2 هذه هي تعبير Infix. لذلك ، تحتاج إلى تحويل تعبير Infix إلى تعبير لاحقة (المعالجة المسبقة) قبل استدعاء Compute () للمشروع الحالي ، ثم استدعاء Compute () للمشروع الحالي. بالطبع ، يمكنك أيضًا تلقي قيمة الإرجاع والقيام بعمليات أخرى ، مثل حفظ الملف (Postprocess). تستخدم هذه العملية وضع الوكيل.
عند استخدام جهاز كمبيوتر ، ستواجه أيضًا تطبيقات وضع الوكيل:
الوكيل البعيد: لا يمكننا الوصول إلى Facebook بسبب GFW في الصين. يمكننا الوصول إليه عن طريق تصفح الجدار (إعداد وكيل). عملية الوصول هي:
(1) يرسل المستخدم طلب HTTP إلى الوكيل
(2) يرسل الوكيل طلب HTTP إلى خادم الويب
(3) يرسل خادم الويب استجابة HTTP إلى الوكيل
(4) يرسل الوكيل استجابة HTTP إلى المستخدم
2. وكيل ثابت
يعني الوكيل الثابت المزعوم أن يتم إنشاء فئة بالوكالة خلال مرحلة التجميع لإكمال سلسلة من العمليات على كائن الوكيل. فيما يلي مخطط فئة الهيكل لنمط الوكيل:
1. المشاركون في نموذج الوكيل
هناك أربعة أدوار في وضع الوكيل:
واجهة الموضوع: أي الواجهة السلوكية التي تنفذها فئة الوكيل.
الكائن الهدف: هذا هو ، الكائن يجري الوكيل.
كائن الوكيل: عميل الوكيل المستخدم لتغليف فئة الموضوع الحقيقية هو ما يلي هو بنية مخطط الفئة لنمط الوكيل:
2. أفكار لتنفيذ نموذج الوكيل
يقوم كل من كائن الوكيل والكائن الهدف بتنفيذ نفس الواجهة السلوكية.
تنفذ فئة الوكيل والفئة الهدف من منطق الواجهة بشكل منفصل.
إنشاء كائن مستهدف في مُنشئ فئة الوكيل.
استدعاء الواجهة السلوكية للكائن الهدف في فئة الوكيل.
إذا أراد العميل استدعاء الواجهة السلوكية للكائن الهدف ، فيمكنه العمل فقط من خلال فئة الوكيل.
3. أمثلة على الوكيل الثابت
فيما يلي مثال تحميل كسول لتوضيح الوكيل الثابت. عندما نبدأ نظام خدمة ، قد يستغرق الأمر وقتًا طويلاً لتحميل فئة معينة. من أجل الحصول على أداء أفضل ، عند بدء تشغيل النظام ، غالبًا ما لا نهيئة هذه الفئة المعقدة ، ولكن بدلاً من ذلك نهيئة فئة الوكيل الخاصة بها. سيؤدي ذلك إلى فصل طرق المستهلكة للموارد باستخدام الوكيل للفصل ، والتي يمكن أن تسرع سرعة بدء تشغيل النظام وتقليل وقت انتظار المستخدم.
تحديد واجهة الموضوع
موضوع الواجهة العامة {public void sealhello () ؛ الفراغ العام saygoodbye () ؛} تحديد فئة مستهدفة وتنفيذ واجهة الموضوع
الطبقة العامة realSubject تنفذ الموضوع {public void sayhello () {system.out.println ("Hello World") ؛ } public void saygoodbye () {system.out.println ("وداعا العالم") ؛ }} تحديد فئة الوكيل لوكالة الكائن الهدف.
الطبقة العامة staticproxy تنفذ الموضوع {private realSubject realSubject = null ؛ public StaticProxy () {} public void sealhello () {// يتم تحميله في ذلك الوقت ، التحميل كسول إذا (realSubject == null) {realSubject = new RealSubject () ؛ } realsubject.sayhello () ؛ } // طريقة Saygoodbye هي نفسها ...} تحديد العميل
عميل الفئة العامة {public static void main (string [] args) {staticproxy sp = new StaticProxy () ؛ sp.sayhello () ؛ sp.saygoodbye () ؛ }}ما ورد أعلاه هو مثال اختبار بسيط للوكيل ثابت. قد لا يشعر بعملية. ومع ذلك ، هذا ليس هو الحال. باستخدام وكيل ، يمكننا أيضًا تحويل طرق الكائن الهدف. على سبيل المثال ، يتم إنشاء سلسلة من الاتصالات في مجموعة اتصال قاعدة البيانات. من أجل ضمان فتح الاتصالات بشكل غير متكرر ، لم يتم إغلاق هذه الاتصالات أبدًا. ومع ذلك ، لدينا دائمًا عادة إغلاق الاتصال المفتوح. وبهذه الطريقة ، يمكننا استخدام وضع الوكيل لإعادة تشغيل الطريقة الإغلاق في واجهة الاتصال ، وتغييره لإعادة تدويره في تجمع اتصال قاعدة البيانات بدلاً من تنفيذ طريقة Connection#الإغلاق فعليًا. هناك العديد من الأمثلة الأخرى ، وتحتاج إلى تجربتها بنفسك.
3. العامل الديناميكي
يشير الوكيل الديناميكي إلى توليد فئات الوكيل ديناميكيًا في وقت التشغيل. وهذا يعني أنه سيتم إنشاء رمز Bytecode لفئة الوكيل وتحميله في وقت التشغيل إلى جهاز تحميل classy من الوكيل الحالي. بالمقارنة مع فئات المعالجة الثابتة ، فإن الفئات الديناميكية لها العديد من الفوائد.
ليست هناك حاجة لكتابة فئة تغليف متطابقة تمامًا للموضوع الحقيقي. إذا كانت هناك العديد من الطرق في واجهة الموضوع ، فمن الصعب أيضًا كتابة طريقة وكيل لكل واجهة. إذا تغيرت الواجهة ، فيجب تعديل الفصول الحقيقية للموضوع والوكالة ، والتي لا تؤدي إلى صيانة النظام ؛
يمكن أن يؤدي استخدام بعض طرق توليد الوكيل الديناميكي إلى صياغة منطق تنفيذ فئة الوكيل في وقت التشغيل ، مما يؤدي إلى تحسين مرونة النظام بشكل كبير.
هناك العديد من الطرق لإنشاء وكيل ديناميكي: يأتي JDK مع الوكيل الديناميكي ، CGLIB ، Javassist ، إلخ. هذه الأساليب لها مزايا وعيوبها. تستكشف هذه المقالة بشكل رئيسي استخدام تحليل الوكيل الديناميكي ومدونة المصدر في JDK.
فيما يلي مثال لشرح استخدام الوكيل الديناميكي في JDK:
الطبقة العامة DynamicProxy تنفذ invocationHandler {private realSubject = null ؛ الكائن العام استدعاء (وكيل الكائن ، طريقة الطريقة ، الكائن [] args) {if (realSubject == null) {realSubject = new RealSubject () ؛ } method.invoke (realSubject ، args) ؛ إرجاع realSubject ؛ }}مثال رمز العميل
عميل الفئة العامة {public static void main (arrings [] args) {موضوع = (موضوع) proxy.newinstance (classloader.getsystemloader () ، realsubject.class.getInterfaces () ، new dynamicproxy ()) ؛ الموضوع. sayhello () ؛ الموضوع. saygoodbye () ؛ }}كما يتضح من الكود أعلاه ، نحتاج إلى استخدام الوكيل الديناميكي في JDK. استخدم الطريقة الثابتة proxy.newinstance (classloader ، واجهات [] ، invokehandler) لإنشاء فئة وكيل ديناميكي. تحتوي طريقة NewInstance على ثلاث معلمات ، تمثل محمل الفئة ، وقائمة من الواجهات التي تريد تنفيذ فئة الوكيل ، ومثيل ينفذ واجهة InvokeHandler. قام الوكيل الديناميكي بتسليم عملية تنفيذ كل طريقة إلى طريقة الاستدعاء للمعالجة.
يتطلب الوكيل الديناميكي JDK أن يكون الوكيل واجهة ، لكن لا يمكن لفئة بسيطة. سترث فئات الوكيل التي تم إنشاؤها بواسطة JDK Dynamic Proxy فئة الوكيل ، وستقوم فئة الوكيل بتطبيق جميع قائمة الواجهة التي مررت بها. لذلك ، يمكن إلقاء النوع على نوع الواجهة. فيما يلي مخطط هيكل الوكيل.
يمكن ملاحظة أن الوكيل هو كل الطرق الثابتة ، لذلك إذا لم تنفذ فئة الوكيل أي واجهة ، فهذا هو نوع الوكيل وليس له طرق مثيل.
بالطبع ، إذا انضممت ، فيجب عليك وكيل فئة لا تنفذ واجهة معينة ، وطرق هذه الفئة هي نفسها التي تحددها واجهات أخرى ، ويمكن تنفيذها بسهولة باستخدام التفكير.
أدوات الطبقة العامة DynamicProxy invokeHandler {// الفئة التي تريد أن تكييفها TargetClass الخاصة = null ؛ . } استدعاء الكائن العام (وكيل الكائن ، طريقة الطريقة ، الكائن [] args) {// استخدم الانعكاس للحصول على الفئة التي تريدها إلى طريقة الوكيل myMethod = targetClass.getClass (). mymethod.setAccessible (صحيح) ؛ إرجاع mymethod.invoke (TargetClass ، args) ؛ }}4.
بعد النظر إلى المثال أعلاه ، نعرف فقط كيفية استخدام الوكيل الديناميكي. ومع ذلك ، لا يزال ضبابيًا حول كيفية إنشاء فئة الوكيل ، والذي يطلق على طريقة Invoke ، إلخ. التحليل التالي
1. كيف يتم إنشاء كائنات الوكيل؟
انظر أولاً إلى الكود المصدري لطريقة proxy.newinstance:
كائن ثابت عام NewProxyInstance (Loader ClassLoader ، فئة <؟> [] واجهات ، invocationHandler H) يلقي alfortleargumentException {} // الحصول على معلومات الواجهة الفئة النهائية <؟> [] intfs = interfaces.clone () ؛ Final SecurityManager sm = system.getSecurityManager () ؛ if (sm! = null) {checkproxyAccess (Reflection.getCallerClass () ، loader ، intfs) ؛ } // إنشاء فئة فئة الوكيل <؟> cl = getProxyClass0 (loader ، intfs) ؛ // ... حسنًا ، دعونا نلقي نظرة على الشوط الأول أولاً}من الكود المصدري ، يمكن ملاحظة أن توليد فئات الوكيل يعتمد على طريقة getProxyClass0. بعد ذلك ، دعونا نلقي نظرة على رمز مصدر getProxyClass0:
الفئة الثابتة الخاصة <؟> getProxyClass0 (loader classloader ، فئة <؟> ... واجهات) {// لا يمكن أن يتجاوز عدد قوائم الواجهة 0xFFFF إذا (interfaces.length> 65535) {رمي newalalargumentException ("الحد الأدنى للواجهة") ؛ } // ملاحظة هنا ، يتم تقديم التفسير التالي بالتفصيل لإرجاع proxyclasscache.get (loader ، interfaces) ؛ } شرح proxyclasscache.get هو: إذا كانت فئة الوكيل التي تنفذ قائمة الواجهة موجودة بالفعل ، فأخذه مباشرة من ذاكرة التخزين المؤقت. إذا لم يكن موجودًا ، يتم إنشاء أحدهم من خلال proxyclassfactory.
قبل النظر إلى الكود المصدري لـ proxyclasscache.get ، دعونا نفهم بإيجاز proxyclasscache:
Private Static Final PremCache <classloader ، class <؟> [] ، class <؟ >> proxyclassCache = New DefenCache <> (New KeyFactory () ، proxyclassfactory ()) ؛
ProxyclassCache هو ذاكرة التخزين المؤقت نوع CreadCache. مُنشئه لديه معلمتان. واحد منهم هو proxyclassfactory المستخدم لإنشاء فئة الوكيل. فيما يلي رمز المصدر لـ proxyclasscache.get:
الفئة النهائية DefenCache <K ، P ، V> {... public v get (k key ، p parameter) {}}هنا تمثل المفتاح ، p يمثل المعلمات ، v تمثل القيمة
public v get (k key ، p parameter) {// java7 nullobject reberge ، إذا كانت المعلمة فارغة ، فسيتم طرح استثناء بالرسالة المحددة. إذا لم يكن فارغا ، ارجع. objects.requirenonnull (المعلمة) ؛ . // الحصول على cachekey من كائن قائمة الانتظار cachekey = cachekey.valueof (المفتاح ، refqueue) ؛ // ملء المورد مع التحميل كسول. المتزامن هو خريطة آمنة مؤشرات ترابط ConcurrentMap <object ، المورد <v>> valuesmap = map.get (cachekey) ؛ if (dordermap == null) {concurrentMap <object ، المورد <v >> oldvaluesmap = map.putifabsent (cachekey ، valuesmap = concurrenthashmap <> ()) ؛ if (oldvaluesmap! = null) {valuesmap = oldvaluesmap ؛ }} // إنشاء مفتاح subkey واسترداد المورد المحتمل <V> المخزّن بواسطة ذلك // subkey from valuesmap object subkey = objects.requirenonnull (subkeyfactory.apply (مفتاح ، المعلمة)) ؛ المورد <v> المورد = قيم map.get (مفتاح subkey) ؛ مصنع المصنع = فارغ ؛ بينما (صحيح) {if (المورد! = null) {// احصل على قيمة من المورد. قد تكون هذه القيمة بمثابة تحقيق مصنع أو ذاكرة التخزين المؤقت. // الجمل الثلاث التالية هي الرمز الأساسي ، الذي يرجع الفئة التي تنفذ InvokeHandler وتحتوي على المعلومات المطلوبة. v value = supplier.get () ؛ if (value! = null) {return value ؛ }} // آخر لا يوجد مورد في ذاكرة التخزين المؤقت // أو المورد الذي تم إرجاعه (يمكن أن يكون cachevalue // أو مصنع لم يكن ناجحًا في تثبيت cachevalue) // العملية التالية هي عملية ملء المورد {// eils} تتمثل وظيفة بينما الحلقة في الحصول على الفئة التي تنفذ بشكل مستمر. يمكن الحصول على هذه الفئة من ذاكرة التخزين المؤقت أو تم إنشاؤها من ProxyFactoryClass.
المصنع هو فئة داخلية تنفذ واجهة المورد <V>. يتجاوز هذه الفئة طريقة الحصول على ، ويتم استدعاء طريقة مثيل من نوع proxyfactoryclass في طريقة GET. هذه الطريقة هي الطريقة الحقيقية لإنشاء فئة وكيل. دعونا نرى الكود المصدري لـ proxyfactoryclass#تطبيق الطريقة:
الطبقة العامة <؟> تطبيق (loader classloader ، فئة <؟> [] واجهات) {map <class <؟> ، boolean> interfaceset = new IdentityHashMap <> (interfaces.length) ؛ لـ (class <؟> intf: interfaces) { /* تحقق من أن محمل الفئة يحل اسم هذه الواجهة إلى نفس كائن الفئة.* / class <؟> interfaceClass = null ؛ حاول {// تحميل معلومات حول كل interfaceClass = class.forname (intf.getName () ، false ، loader) ؛ } catch (classnotfoundException e) {} // إذا كانت الفئة المحملة مع فئة الحمولة الخاصة بك لا تساوي الفئة التي مررت بها ، رمي استثناء إذا (interfaceClass! = intf) {رمي new alfictalumentexception (intf + "غير مرئي من تحميل الفئة") ؛ } // إذا لم يكن الوارد نوع واجهة إذا كان (! interfaceClass.isinterface ()) {رمي New IllugalArgumentException (interfaceClass.getName () + "ليس واجهة") ؛ } // تحقق مما إذا كانت الواجهة متكررة إذا (interfaceset.put (interfaceClass ، boolean.true)! = null) {رمي new alficalArgumentException ("الواجهة المتكررة:" + interfaceClass.getName ()) ؛ }} سلسلة proxypkg = null ؛ // حزمة لتحديد فئة الوكيل في /* سجل حزمة واجهة الوكيل غير العامة بحيث يتم تعريف فئة الوكيل في نفس الحزمة. * تحقق من أن جميع واجهات الوكيل غير العامة موجودة في نفس الحزمة. *//تعتمد هذه الفقرة على ما إذا كانت هناك واجهات غير عامة في الواجهة التي مررت بها. إذا كان الأمر كذلك ، فيجب تحديد كل هذه الواجهات في حزمة واحدة. خلاف ذلك ، رمي استثناءات (الفئة <؟> intf: interfaces) {int flags = intf.getModifiers () ؛ if (! modifier.ispublic (flags)) {string name = intf.getName () ؛ int n = name.lastindexof ('.') ؛ String pkg = ((n == -1)؟ "": name.subString (0 ، n + 1)) ؛ if (proxypkg == null) {proxypkg = pkg ؛ } if if (! pkg.equals (proxypkg)) {رمي جديد alficalArgumentException ("واجهات غير عامة من حزم مختلفة") ؛ }}}} if (proxypkg == null) {// إذا لم يكن هناك واجهات الوكيل غير الحكومية ، استخدم proxypkg proxypkg com.sun.proxy com.sun.proxy proxypkg = respectutil.proxy_package + "." ؛ } / * * اختر اسمًا لفئة الوكيل لإنشاء. */ long num = nextUniquenumber.getandincrement () ؛ // إنشاء اسم فئة فئة الوكيل العشوائي ، $ proxy + num String proxyname = proxypkg + proxyclassnamePrefix + num ؛ /** قم بإنشاء ملف الفئة لفئة الوكيل ، وإرجاع دفق البايت*/ byte [] proxyclassfile = proxygenerator.generateproxyclass (proxyname ، interfaces) ؛ حاول {return defedeclass0 (loader ، proxyname ، proxyclassfile ، 0 ، proxyclassfile.length) ؛ } catch (classformaterror e) {// end refl new alficalArgumentException (e.ToString ()) ؛ }}}إن proxyfactoryclass#المذكورة أعلاه هو طريقة لإنشاء فئات وكيل ، وهي في الواقع غير دقيقة. بعد قراءة الكود المصدري هنا ، سنجد أن proxygenerator#generateproxyclass هي الطريقة لإنشاء فئات الوكيل حقًا. قم بإنشاء ملف الفئة المقابل وفقًا لتكوين فئة Java Bytecode (انظر مقالتي الأخرى Java Bytecode Learning Notes). رمز المصدر المحدد لـ ProxyGenerator#generateproxyclass هو كما يلي:
بايت خاص [] generateClassFile () { / * * الخطوة 1: تجميع كائنات proxymethod لجميع الطرق لإنشاء رمز إرسال الوكيل ل. *// AddProxymethod Method هي إضافة جميع الطرق إلى قائمة وتتوافق مع الفئة المقابلة // فيما يلي ثلاث طرق مقابلة للكائن ، tostring و addproxymethod (hashcodemethod ، object.class) ؛ addproxymethod (equalsmethod ، object.class) ؛ addproxymethod (ToStringMethod ، object.class) ؛ // قارن الواجهة في قائمة الواجهة مع الطرق الموجودة أسفل الواجهة (int i = 0 ؛ i <interfaces.length ؛ i ++) {method [] methods = interfaces [i] .getMethods () ؛ لـ (int j = 0 ؛ j <methods.length ؛ j ++) {addProxymethod (الطرق [j] ، واجهات [i]) ؛ }} / * * لكل مجموعة من أساليب الوكيل بنفس التوقيع ، تحقق من أن أنواع إرجاع الأساليب متوافقة. */ for (list <Proxymethod> signmethods: proxymethods.values ()) {checkReturnTypes (sigmethods) ؛ } / * * الخطوة 2: تجميع FieldInfo و methodinfo هياكل لجميع الحقول والطرق في الفصل الذي نولده. *//أضف طريقة مُنشئ إلى الطريقة ، والتي ليست سوى مُنشئ واحد ، وهو مُنشئ مع واجهة InvocationHandler. ومع ذلك ، لم تتم معالجتها بعد. تمت إضافته أولاً وانتظر الحلقة. وصف اسم المُنشئ في ملف الفئة هو <IRIT> TREE {Methods.add (cenerateConstructor ()) ؛ لـ (قائمة <Proxymethod> signmethods: proxymethods.values ()) {for (proxymethod pm: signmethods) {// إضافة سمة نوع الطريقة إلى كل طريقة وكيل. الرقم 10 هو معرف ملف الفئة ، مما يعني أن هذه السمات هي حقول. // أضف كل طريقة وكيل إلى طريقة فئة الوكيل. }} // إضافة كتلة تهيئة ثابتة وتهيئة كل سمة. هنا ، تسمى كتلة التعليمات البرمجية الثابتة أيضًا مُنشئ الفئة. إنها في الواقع طريقة تحمل اسم <Clinit> ، لذا أضفها إلى طريقة قائمة الطريقة. } catch (ioException e) {رمي internalerror جديد ("استثناء غير متوقع I/O") ؛ } // لا يمكن أن يتجاوز عدد الطرق والسمات 65535 ، بما في ذلك العدد السابق للواجهات. // هذا لأنه في ملف الفئة ، يتم تمثيل هذه الأرقام في سداسي عشري 4 بت ، وبالتالي فإن القيمة القصوى هي 2 إلى قوة 16 -1 إذا (methods.size ()> 65535) {رمي جديد غير aluallargumentexception ("الحد الأدنى للأسلوب") ؛ } if (fields.size ()> 65535) {رمي new alficalArgumentException ("الحد من الحقل تجاوز") ؛ }. لن أخوض في التفاصيل. إذا كنت في حاجة إليها ، يمكنك الرجوع إلى المعرفة ذات الصلة من JVM Virtual Machine Bytecode. cp.getClass (dottoslash (className)) ؛ cp.getClass (superclassname) ؛ لـ (int i = 0 ؛ i <interfaces.length ؛ i ++) {cp.getClass (dottoslash (interfaces [i] .getName ())) ؛ } cp.setReadOnly () ؛ bytearrayoutputstream bout = new bytearrayoutputStream () ؛ DataOutputStream Dout = جديد DataOutputStream (bout) ؛ حاول {// u4 magic ؛ dout.writeint (0xcafebabe) ؛ // u2 minor_version ؛ dout.writeshort (classfile_minor_version) ؛ // u2 major_version ؛ dout.writeshort (classfile_major_version) ؛ cp.write (dout) ؛ // (اكتب مجموعة ثابتة) // u2 Access_Flags ؛ dout.writeshort (acc_public | acc_final | acc_super) ؛ // u2 this_class ؛ dout.writeshort (cp.getClass (dottoslash (className))) ؛ // u2 super_class ؛ dout.writeshort (cp.getClass (superclassName)) ؛ // u2 interfaces_count ؛ dout.writeshort (interfaces.length) ؛ // u2 interfaces [interfaces_count] ؛ لـ (int i = 0 ؛ i <interfaces.length ؛ i ++) {dout.writeshort (cp.getClass (dottoslash (interfaces [i] .getName ()))) ؛ } // u2 fields_count ؛ dout.writeshort (fields.size ()) ؛ // field_info fields [fields_count] ؛ لـ (fieldInfo f: fields) {f.write (dout) ؛ } // u2 mays_count ؛ dout.writeshort (methods.size ()) ؛ // method_info Methods [MOTEES_COUNT] ؛ لـ (MethodInfo M: Methods) {M.Write (dout) ؛ } // u2 attributes_count ؛ dout.writeshort (0) ؛ // (لا توجد سمات classfile لفئات الوكيل)} catch (ioException e) {رمي internalerror جديد ("استثناء I/O غير متوقع") ؛ } return bout.tobytearray () ؛ }بعد طبقات المكالمات ، يتم إنشاء فئة وكيل أخيرًا.
2. من دعا الاستدعاء؟
نقوم بمحاكاة JDK لإنشاء فئة بالوكالة بمفرده ، مع اسم الفصل TestProxygen:
الطبقة العامة testgeneratorproxy {public static void main (string [] args) يلقي ioException {byte [] classfile = proxygenerator.generateproxyclass ("testproxygen" ، thision.class.getInterfaces ()) ؛ ملف ملف = ملف جديد ("/المستخدمين/yadoao/desktop/testproxygen.class") ؛ fileOutputStream fos = new FileOutputStream (ملف) ؛ fos.write (classfile) ؛ fos.flush () ؛ fos.close () ؛ }}قم بإلغاء توضيح ملف الفصل مع JD-Gui ، والنتيجة هي كما يلي:
استيراد com.dynamicproxy.isubject ؛ استيراد java.lang.reflect.invocationHandler ؛ استيراد java.lang.reflect.method ؛ import java.lang.reflect.proxy M3 ؛ الطريقة الثابتة الخاصة M1 ؛ الطريقة الثابتة الخاصة M0 ؛ الطريقة الثابتة الخاصة M4 ؛ الطريقة الثابتة الخاصة M2 ؛ TestProxygen العام (InvocationHandler ParamInvocationHandler) يلقي {super (paraminvocationHandler) ؛ } الفراغ النهائي العام sealhhello () يلقي {try {this.h.invoke (this ، m3 ، null) ؛ يعود؛ } catch (error | runTimeException localerror) {throw localerror ؛ } catch (throwable localThrowable) {رمي جديد غير محدد غير قابل للمعاناة (localThrowable) ؛ }} Public Final Boolean Equals (Object ParamObject) يلقي {try {return ((boolean) this.h.invoke (this ، m1 ، comple new [] {paramobject})). booleanvalue () ؛ } catch (error | runTimeException localerror) {throw localerror ؛ } catch (throwable localThrowable) {رمي جديد غير محدد غير قابل للمعاناة (localThrowable) ؛ }} public final int hashcode () رمي {try {return ((integer) this.h.invoke (this ، m0 ، null)). Intvalue () ؛ } catch (error | runTimeException localerror) {throw localerror ؛ } catch (throwable localThrowable) {رمي جديد غير محدد غير قابل للمعاناة (localThrowable) ؛ }} public final void saygoodbye () رمي {try {this.h.invoke (this ، m4 ، null) ؛ يعود؛ } catch (error | runTimeException localerror) {throw localerror ؛ } catch (throwable localThrowable) {رمي جديد غير محدد غير قابل للمعاناة (localThrowable) ؛ }} السلسلة النهائية العامة tostring () يلقي {try {return (string) this.h.invoke (this ، m2 ، null) ؛ } catch (error | runTimeException localerror) {throw localerror ؛ } catch (throwable localThrowable) {رمي جديد غير محدد غير قابل للمعاناة (localThrowable) ؛ }} static {try {m3 = class.forname ("com.su.dynamicproxy.isubject"). getMethod ("sayhello" ، فئة جديدة [0]) ؛ m1 = class.forname ("java.lang.object"). getMethod ("equals" ، فئة جديدة [] {class.forname ("java.lang.object")}) ؛ m0 = class.forname ("java.lang.object"). getMethod ("hashcode" ، فئة جديدة [0]) ؛ m4 = class.forname ("com.su.dynamicproxy.isubject"). getMethod ("Saygoodbye" ، فئة جديدة [0]) ؛ m2 = class.forname ("java.lang.object"). getMethod ("toString" ، فئة جديدة [0]) ؛ يعود؛ } catch (nosuchmethodexception localnosuchmethodexception) {رمي nosuchmethoderror جديد (localnosuchmethodexception.getMessage ()) ؛ } catch (classNotFoundException localClassNotFoundException) {رمي noclassDeffounderror (localclassnotfoundException.getMessage ()) ؛ }}} أولاً ، لاحظت أن مُنشئ فئة الوكيل التي تم إنشاؤها في فئة تنفذ واجهة InvokeHandler كمعلمة ، ويسمى مُنشئ وكيل الفئة الأصل ، والذي قام بتهيئة متغير العضو المحمي Invokehander H في الوكيل.
لقد لاحظت العديد من كتل التهيئة الثابتة مرة أخرى. تتمثل كتلة التهيئة الثابتة هنا في تهيئة قائمة واجهة الوكيل والأساليب hashcode و tostring ومساواة.
أخيرًا ، هناك عملية استدعاء لهذه الأساليب ، وكلها تراجع إلى طريقة الاستدعاء.
هذا ينتهي مع تحليل هذا النمط الوكيل.