مقدمة
ينشأ "Dynamic Agent" فعليًا من وضع الوكيل في وضع التصميم ، ويستخدم وضع الوكيل كائن الوكيل لإكمال طلبات المستخدم وحظر وصول المستخدمين إلى كائنات حقيقية.
لإعطاء أبسط مثال ، نريد "FQ" للوصول إلى مواقع الويب الأجنبية ، لأنه لا يوجد لدينا كل عناوين IP أجنبية ، يمكنك إرسال بيانات الطلب الخاصة بك إلى تلك المضيفين الأجانب غير المحظورة ، ثم تقوم بإعادة توجيه الطلب إلى الوجهة من خلال تكوين المضيف الأجنبي وإعادة توجيهه إلى مضيفنا المحلي بعد تلقي رسالة الاستجابة.
في هذا المثال ، يعد المضيف الأجنبي كائنًا وكيلًا ، وتلك المضيفات التي يتم إسقاطها بواسطة الجدار هي كائنات حقيقية. لا يمكننا الوصول مباشرة إلى الكائنات الحقيقية ، لكن يمكننا الوصول إليها بشكل غير مباشر من خلال وكيل.
تتمثل إحدى ميزة وضع الوكيل في أن جميع الطلبات الخارجية تمر عبر كائن الوكيل ، وأن كائن الوكيل له الحق في التحكم في ما إذا كان يُسمح لك بالوصول إلى الكائن الحقيقي حقًا. إذا كان هناك طلب غير قانوني ، فيمكن لكائن الوكيل أن يرفضك تمامًا دون أي مشكلة في الكائن الحقيقي.
أحد أكثر التطبيقات النموذجية لوضع الوكيل هو إطار الربيع. يستخدم AOP من الربيع البرمجة الموجهة نحو الجانب لعزل منطق العمل الفعلي من استثناءات السجل ذات الصلة والمعلومات الأخرى. في كل مرة تطلب فيها منطق العمل ، فإنه يتوافق مع كائن وكيل. بالإضافة إلى إجراء عمليات التحقق من الإذن اللازمة وطباعة السجل ، فإن هذا الكائن الوكيل هو كتلة معالجة منطق الأعمال الحقيقية.
وكيل ثابت
هناك نوعان من المنفذين الرئيسيين لنموذج الوكيل ، "الوكيل الثابت" و "الوكيل الديناميكي". الفرق الأساسي بين هذين الاثنين هو أن فئة الوكيل السابقة تتطلب الترميز اليدوي من قبل المبرمجين ، بينما يتم إنشاء فئة الوكيل الأخيرة تلقائيًا. لذلك ، هذا هو السبب في أنك بالكاد سمعت عن مفهوم "الوكيل الثابت". بالطبع ، من الأسهل فهم "الوكيل الديناميكي".
أحد الأشياء التي تحتاج إلى أن تكون واضحًا هي أن وكيل الوكيل وكلاء الكائن الحقيقي ، أي أن الكائن الوكيل يحتاج إلى توفير نفس اسم الطريقة على الأقل ككائن حقيقي للاتصال ، لذلك يحتاج كائن الوكيل إلى تحديد جميع الطرق المملوكة للكائن الحقيقي ، بما في ذلك الأساليب في الفئة الأصل.
دعونا نلقي نظرة على مثال وكيل ثابت بسيط:
لتوضيح المشكلة ، نحدد واجهة iservice وندع فئتنا الحقيقية ترث الواجهة وتنفيذها ، بحيث يكون هناك طريقتان في فئتنا الحقيقية.
فكيف ينبغي تعريف فئة الوكيل لإكمال الوكيل للكائنات الحقيقية؟
بشكل عام ، يتمثل جوهر فئة الوكيل في تحديد جميع الطرق في الفئة الحقيقية وإضافة بعض العمليات الأخرى داخل الطريقة ، وأخيراً استدعاء طريقة الفئة الحقيقية.
تحتاج فئة الوكيل إلى الوصي على جميع الأساليب في الفئة الحقيقية ، أي الأساليب التي هي بالضبط مثل تلك الأساليب في الفئة الحقيقية ، وسيتم تسمى طريقة الطبقة الحقيقية بشكل غير مباشر داخل هذه الطرق.
عمومًا ، ستختار فئة الوكيل أن ترث جميع الواجهات والفئات الوالدين للفئة الحقيقية مباشرة من أجل الحصول على جميع أساليب الوالدين في الفئة الحقيقية ، أي أول أساليب الوالدين المباشرة.
بعد ذلك ، الوكيل ليس طريقة الوالدين في الفئة الحقيقية. في المثال هنا ، فإن طريقة Doservice هي طريقة الفئة الحقيقية. يحتاج فئة الوكيل الخاصة بنا أيضًا إلى تحديد طريقة بنفس الطريقة لتوقيعها.
وبهذه الطريقة ، حتى إذا تم الانتهاء من فئة الوكيل الخاصة بنا ، يمكن أن يتم تكييف جميع الطرق في الفصل الحقيقي من خلال فئة الوكيل في المستقبل. مثله:
public static void main (string [] args) {RealClass RealClass = new RealClass () ؛ proxyclass proxyclass = new proxyclass (RealClass) ؛ proxyclass.sayhello () ؛ proxyclass.doservice () ؛}يمكن لـ ProxyClass ، ككائن فئة من الوكيل ، أن يحقق جميع الطرق في الفئة الحقيقية ، وطباعة بعض المعلومات "غير المهمة" قبل تنفيذ هذه الطرق.
هذا هو في الأساس فكرة التنفيذ الأساسية لنموذج الوكيل ، ولكن الفرق بين الوكيل الديناميكي وهذا النوع من الوكيل الثابت هو أن الوكيل الديناميكي لا يتطلب تعريفات طريقتنا واحدًا تلو الآخر ، وسيقوم الجهاز الافتراضي بإنشاء هذه الطرق تلقائيًا لك.
آلية الوكيل الديناميكي JDK
ما يميز الوكيل الديناميكي عن الوكيل الثابت هو أن فئة الوكيل من الوكيل الديناميكي يتم إنشاؤها ديناميكيًا بواسطة الجهاز الظاهري في وقت التشغيل وتم مسحها عند إلغاء تثبيت الجهاز الظاهري.
نقوم بإعادة استخدام الفصول المستخدمة في الوكيل الثابت أعلاه لنرى كيف يمكن للوكيل الديناميكي لـ JDK أن يحقق جميع طرق مثيل لفئة معينة.
تحديد فئة معالجة المعالج:
يستدعي واجهة برمجة تطبيقات الوكيل الديناميكي في الوظيفة الرئيسية JDK لإنشاء مثيل فئة الوكيل:
لا يزال هناك الكثير من التعليمات البرمجية المعنية ، دعنا نحللها شيئًا فشيئًا. أولاً ، تقوم RealClass بتنفيذ واجهة iservice باعتبارها فئة الوكيل الخاصة بنا وتعرّف طريقتها الخاصة داخليًا.
بعد ذلك ، نحدد فئة المعالجة التي ترث واجهة InvocationHandler وتنفذ طريقة الاستدعاء المعلنة بشكل فريد. بالإضافة إلى ذلك ، يتعين علينا أن نعلن حقل عضو لتخزين كائنات حقيقية ، أي كائنات وكيل ، لأن أي طريقة نقوم بالوكالة تعتمد بشكل أساسي على الأساليب ذات الصلة للكائنات الحقيقية.
فيما يتعلق بدور هذه الطريقة الاستدعاء وأهمية المعلمات الرسمية المختلفة ، سنقوم بإجراء تحليل مفصل عندما نعكس رمز مصدر الوكيل.
أخيرًا ، حدد فئة المعالجة الخاصة بنا وأداء الوكيل الديناميكي بشكل أساسي استنادًا إلى JDK. الطريقة الأساسية هي طريقة NewProxyInstance لفئة الوكيل. هذه الطريقة لها ثلاثة معلمات. واحد هو محمل فئة ، والثاني عبارة عن مجموعة من جميع الواجهات التي تنفذها فئة الوكيل ، والثالث هو فئة المعالج المخصصة لدينا.
يستخدم الجهاز الظاهري جهاز تحميل الفئة الذي تقدمه في وقت التشغيل ، ويقوم بتحميل جميع فئات الواجهة المحددة في منطقة الطريقة ، ثم يعكس ويقرأ الطرق في هذه الواجهات ويجمع بين فئة المعالج لإنشاء نوع وكيل.
الجملة الأخيرة قد تكون مجردة بعض الشيء. كيف "جنبا إلى جنب مع فئات المعالج لإنشاء نوع الوكيل"؟ في هذا الصدد ، نحدد معلمات بدء تشغيل الجهاز الظاهري وندعه يحفظ ملف الفئة الذي تم إنشاؤه لفئة الوكيل.
-dsun.misc.proxygenerator.SavegeneredFiles = true
نقوم بإلغاء توضيح ملف الفئة هذا من خلال أدوات الطرف الثالث ، وهناك الكثير من المحتوى. نقسم التحليل:
بادئ ذي بدء ، اسم فئة الوكيل هذه عشوائية للغاية. إذا كان هناك عدة فئات وكيل يتم إنشاؤها في برنامج ، فإن "$ proxy + number" هو اسم الفصل.
بعد ذلك ، ستلاحظ أن فئة الوكيل هذه ترث فئة الوكيل والواجهة المتساقطة التي حددناها (سابقًا إذا تم تحديد واجهات متعددة ، فسيتم مورث واجهات متعددة هنا).
بعد ذلك ، ستجد أن هذا المُنشئ يتطلب معلمة نوع الاحتجاج ، وأن جسم المنشئ هو تمرير مثيل InvocationHandler هذا إلى الحقل المقابل لوكيل الفئة الأصل للتخزين. هذا أيضًا أحد الأسباب التي تجعل جميع فئات الوكيل يجب أن تستخدم الوكيل كفئة الوالدين ، والتي تتمثل في نشر حقل InvocationHandler في فئة الأصل. سنعرف لاحقًا أن هذا التصميم الصغير سيؤدي إلى عيب مميت للوكالة الديناميكية المستندة إلى JDK ، والتي سيتم تقديمها لاحقًا.
هذا المحتوى هو أيضًا جزء مهم نسبيًا من فئة الوكيل. سيتم تنفيذها عندما يقوم الجهاز الظاهري بتهيئة فئة الوكيل بشكل ثابت. تكمل هذا الكود الكبير وظيفة عكس جميع الطرق في الواجهة ، ويتم تخزين جميع الطرق المنعكسة المقابلة لحقل من نوع الطريقة.
بالإضافة إلى ذلك ، يعكس الجهاز الظاهري أيضًا ثلاث طرق شائعة في الكائن ، أي أن فئة الوكيل ستقوم أيضًا بتكليف الكائن الحقيقي الموروث من الكائن.
في الجزء الأخير ، ما نراه هو أن الجهاز الظاهري يعكس جميع الأساليب المراد أن يتم وكيلها بناءً على كتلة رمز التهيئة الثابتة وتولد طرق الوكيل لهم.
تبدو هذه الأساليب الكثير من التعليمات البرمجية ، لكنها في الواقع ليست سوى سطر واحد من التعليمات البرمجية ، والذي يخرج فئة المعالج المخزنة أثناء إنشاء مثيل للوكيل من وكيل الفئة الأصل ويطلق على طريقة الاستدعاء.
معلمات الطريقة هي نفسها في الأساس. المعلمة الأولى هي مثيل فئة الوكيل الحالي (يثبت أنه من غير المجدي تمرير هذه المعلمة في الماضي) ، والمعلمة الثانية هي مثيل طريقة الطريقة ، والمعلمة الثالثة هي مجموعة المعلمة الرسمية للطريقة. إذا لم يكن كذلك ، فهو لاغ.
الآن دعونا نلقي نظرة على فئة المعالج المخصصة:
ستستدعي جميع أساليب فئة الوكيل طريقة استدعاء فئة المعالج وتمر بالطريقة الحالية لفئة الوكيل. يمكن أن تختار طريقة استدعاء هذه الطريقة بشكل طبيعي ، أو تخطي استدعاء الطريقة ، وحتى القيام ببعض الأشياء الإضافية قبل وبعد أن يتم استدعاء الطريقة بالفعل.
هذه هي الفكرة الأساسية للوكيل الديناميكي JDK. دعنا نلخص بإيجاز عملية الاتصال بأكملها.
بادئ ذي بدء ، يعد تعريف فئة المعالج أمرًا ضروريًا ، ويجب أن يرتبط بكائن حقيقي ، أي مثيل فئة الوكيل.
بعد ذلك ، نسمي أي طريقة لفئة الوكيل من الخارج ، ومن رمز المصدر الذي تم فكّره ، نعلم أن طريقة فئة الوكيل ستستدعي بدلاً من ذلك طريقة استدعاء المعالج وتمر في مجموعة توقيع الأسلوب والمعلمة الرسمية.
أخيرًا ، ما إذا كان يمكن استدعاء الطريقة التي يمكن أن تسمى عادةً على ما إذا كان المعالج يستدعي أن يكون الجسم يدعو بالفعل طريقة الطريقة.
في الواقع ، فإن الوكيل الديناميكي الذي تم تنفيذه على أساس JDK معيب ، وهذه العيوب ليس من السهل إصلاحها ، لذلك CGLIB شائع.
بعض العيوب وأوجه القصور
آلية وكيل واحد
لا أعرف ما إذا كنت لاحظت أن الأمثلة أعلاه غير متوفرة. يرث فئة الوكيل التي تم إنشاؤها بواسطة الجهاز الظاهري فئة الوكيل من أجل تخزين مثيلات فئة المعالج الخاصة بها علنًا. ماذا يعني ذلك؟
يخبرك ميراث جافا الجذر الفردي أن فئة الوكيل لم تعد قادرة على ورث أي فئة أخرى ، وبالتالي فإن الأساليب في الفئة الأم لفئة الوكيل لن تتمكن بشكل طبيعي من الحصول عليها ، أي أن فئة الوكيل لا يمكنها أن تستند إلى أي طرق للفئة الأم في الفئة الحقيقية.
بصرف النظر عن هذا هو التفاصيل الصغيرة الأخرى. أتساءل عما إذا كنت قد لاحظت ذلك. لقد كتبت مثل هذا.
طريقة Sayhello هنا هي الواجهة التي تم تنفيذها ، في حين أن طريقة Doservice هي طريقة تنتمي إلى RealClass. لكننا لا نرى هذه الطريقة من فئة الوكيل ، مما يعني أن هذه الطريقة ليست وكيل.
لذلك ، فإن آلية الوكيل الديناميكي لـ JDK مفردة ، ولا يمكنها إلا أن تكون الوكيل في مجموعة الواجهة لفئة الوكيل.
قيمة الإرجاع غير الودية
يرجى ملاحظة أن NewProxyInstance يعيد مثيلًا لفئة الوكيل "$ proxy0" ، ولكن يتم إرجاعه كنوع الكائن ، ولا يمكنك إجبار مثيل الكائن على نوع "$ proxy0".
على الرغم من أننا نعلم أن مثيل الكائن هذا هو في الواقع نوع "$ proxy0" ، فإن نوع "$ proxy0" غير موجود خلال فترة التجميع ، ولن يسمح لك المترجم بطبيعة الحال بفرضه على نوع غير موجود. لذلك ، لن يجبرها بشكل عام على أن تكون واحدة من الواجهات التي تنفذها فئة الوكيل هذه.
RealClass rc = new RealClass () ؛ myhanlder hanlder = new myhanlder (rc) ؛ iservice obj = (iservice) proxy.newproxyinstance (rc.getClass ().
إخراج تشغيل البرنامج:
بداية الوكيل ...... مرحبا العالم ... وكيل نهاية ......
ثم يأتي السؤال مرة أخرى. إذا قام فئة الوكيل الخاصة بنا بتنفيذ واجهات متعددة ، فما نوع الواجهة الذي يجب أن تجبره على ذلك؟ الآن ، على افتراض أن فئة الوكيل تنفذ واجهات A و B ، ثم إذا تم إجبار الحالة الأخيرة على A ، فلا يمكنك استدعاء جميع الطرق في الواجهة B التي تم تنفيذها بواسطة فئة الوكيل ، والعكس صحيح.
هذا يؤدي مباشرة إلى نتيجة. عليك أن تعرف أي طريقة في أي واجهة. إذا كنت تجبرها على الواجهة المقابلة قبل استدعاء الطريقة ، فهي غير ودية تمامًا.
ما سبق هو ما نعتقد أنه ليس أنيقًا في آلية الوكيل الديناميكي القائم على JDK. بالطبع ، مزاياها بالتأكيد أكبر من هذه العيوب. في المقالة التالية ، سنقدم مكتبة الوكيل الديناميكية CGLIB تستخدم على نطاق واسع من قبل الأطر المختلفة. تعتمد طبقتها الأساسية على إطار عمل Bytecode ASM ولم تعد تعتمد على الميراث لتنفيذها ، وحل أوجه القصور في الوكيل الفردي لـ JDK.
يتم تخزين جميع الرموز والصور والملفات في المقالة في السحابة على جيثب:
(https://github.com/singleyam/overview_java)
يمكنك أيضًا اختيار التنزيل محليًا.
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.