جافا الوكيل الديناميكي
وضع الوكيل
نمط الوكيل هو نمط تصميم Java شائع الاستخدام، وخصائصه هي أن فئة الوكيل وفئة المفوض لهما نفس الواجهة، وفئة الوكيل مسؤولة بشكل أساسي عن المعالجة المسبقة للرسائل الخاصة بفئة المفوض، وتصفية الرسائل، وإعادة توجيه الرسائل إلى فئة المفوض ومعالجة الرسائل بعد ذلك. عادة ما يكون هناك ارتباط بين فئة الوكيل وفئة المفوض، حيث يرتبط كائن فئة الوكيل بكائن فئة الوكيل نفسه، ولا يقوم بتنفيذ الخدمة فعليًا، ولكن عن طريق استدعاء الأساليب ذات الصلة كائن فئة المفوض تقديم خدمات محددة.
وفقًا لفترة إنشاء الوكيل، يمكن تقسيم فئة الوكيل إلى نوعين.
الوكيل الثابت: يتم إنشاؤه بواسطة المبرمجين أو يتم إنشاؤه تلقائيًا بواسطة أداة معينة ثم يتم تجميعه. قبل تشغيل البرنامج، يكون الملف .class لفئة الوكيل موجودًا بالفعل.
الوكيل الديناميكي: يتم إنشاؤه ديناميكيًا باستخدام آلية الانعكاس عند تشغيل البرنامج.
قم أولاً بإلقاء نظرة على الوكيل الثابت:
1.Count.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.dao؛
/**
* تحديد واجهة الحساب
*
* @ المؤلف المسؤول
*
*/
عدد الواجهة العامة {
// عرض طريقة الحساب
استعلام الفراغ العام () ؛
// تعديل طريقة الحساب
التحديث الفراغي العام () ؛
}
2.CountImpl.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.dao.impl؛
import net.battier.dao.Count;
/**
* فئة المندوب (بما في ذلك منطق الأعمال)
*
* @ المؤلف المسؤول
*
*/
الطبقة العامة CountImpl تنفذ Count {
@تجاوز
عدد الاستعلامات الفارغة العامة () {
System.out.println("عرض طريقة الحساب...");
}
@تجاوز
تحديث الفراغ العام () {
System.out.println("تعديل طريقة الحساب...");
}
}
CountProxy.java
الحزمة net.battier.dao.impl؛
import net.battier.dao.Count;
/**
* هذه فئة وكيل (فئة تنفيذ CountImpl محسنة)
*
* @ المؤلف المسؤول
*
*/
الطبقة العامة CountProxy تنفذ Count {
الخاص CountImpl countImpl؛
/**
* تجاوز المنشئ الافتراضي
*
* @param countImpl
*/
CountProxy العام (CountImpl countImpl) {
this.countImpl = countImpl;
}
@تجاوز
عدد الاستعلامات الفارغة العامة () {
System.out.println("قبل معالجة المعاملة");
// استدعاء طريقة فئة المفوض؛
countImpl.queryCount();
System.out.println("بعد معالجة المعاملة");
}
@تجاوز
تحديث الفراغ العام () {
System.out.println("قبل معالجة المعاملة");
// استدعاء طريقة فئة المفوض؛
countImpl.updateCount();
System.out.println("بعد معالجة المعاملة");
}
}
3.TestCount.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.test؛
import net.battier.dao.impl.CountImpl;
import net.battier.dao.impl.CountProxy;
/**
* فئة اختبار العد
*
* @ المؤلف المسؤول
*
*/
فئة عامة TestCount {
public static void main(String[] args) {
CountImpl countImpl = new CountImpl();
CountProxy countProxy = new CountProxy(countImpl);
countProxy.updateCount();
countProxy.queryCount();
}
}
من خلال مراقبة الكود، يمكنك أن تجد أن كل فئة وكيل يمكن أن تخدم واجهة واحدة فقط، وبهذه الطريقة، سيتم حتماً إنشاء عدد كبير جدًا من الوكلاء أثناء تطوير البرنامج، علاوة على ذلك، فإن جميع عمليات الوكيل هي نفسها باستثناء الطريقة التي يتم استدعاؤها يجب أن تتكرر في هذا الوقت. أفضل طريقة لحل هذه المشكلة هي استخدام فئة وكيل لإكمال جميع وظائف الوكيل. في هذه الحالة، يجب استخدام وكيل ديناميكي لإكمالها.
دعونا نلقي نظرة على الوكيل الديناميكي:
يحتوي الوكيل الديناميكي JDK على فئة وواجهة:
واجهة معالج الاستدعاء:
الواجهة العامة InvocationHandler {
استدعاء الكائن العام (وكيل الكائن، طريقة الطريقة، كائن [] args) يلقي Throwable؛
}
وصف المعلمة:
وكيل الكائن: يشير إلى الكائن الذي يتم وكيله.
طريقة الطريقة: الطريقة التي سيتم استدعاؤها
وسيطات الكائن []: المعلمات المطلوبة عند استدعاء الطريقة
يمكنك التفكير في فئة فرعية من واجهة InvocationHandler باعتبارها فئة التشغيل النهائية للوكيل، لتحل محل ProxySubject.
فئة الوكيل:
فئة الوكيل هي فئة متخصصة في عمليات الوكيل. يمكن استخدام هذه الفئة لإنشاء فئات التنفيذ لواجهة واحدة أو أكثر ديناميكيًا. توفر هذه الفئة طرق التشغيل التالية:
كائن ثابت عام newProxyInstance (محمل ClassLoader، واجهات Class<?>[]،
معالج الاستدعاء ح)
يرمي IllegalArgumentException
وصف المعلمة:
محمل ClassLoader: محمل الفئة
واجهات الفئة<?>[]: احصل على كافة الواجهات
InvocationHandler h: احصل على مثيل الفئة الفرعية لواجهة InvocationHandler
ملاحظة: محمل الفئة
مطلوب مثيل لفئة ClassLoader في طريقة newProxyInstance () في فئة Proxy. ClassLoader يتوافق فعليًا مع محمل الفئة. هناك ثلاثة محمل للفئة الرئيسية في Java؛
Booststrap ClassLoader: هذا المُحمل مكتوب بلغة C++ ولا يمكن رؤيته في التطوير العام؛
ملحق ClassLoader: يُستخدم لتحميل فئات الامتدادات، والتي تتوافق بشكل عام مع الفئات الموجودة في دليل jre/lib/ext؛
AppClassLoader: (افتراضي) يقوم بتحميل الفئة المحددة بواسطة classpath، وهو المُحمل الأكثر استخدامًا.
وكيل ديناميكي
على النقيض من فئة الوكيل الثابتة، هناك فئة الوكيل الديناميكي. يتم إنشاء الرمز الثانوي لفئة الوكيل الديناميكي ديناميكيًا بواسطة آلية انعكاس Java عند تشغيل البرنامج، دون الحاجة إلى كتابة كود المصدر الخاص به يدويًا. لا تعمل فئات الوكيل الديناميكي على تبسيط عمل البرمجة فحسب، بل تعمل أيضًا على تحسين قابلية التوسع لأنظمة البرامج، لأن آلية انعكاس Java يمكنها إنشاء أي نوع من فئات الوكيل الديناميكي. توفر فئة Proxy وواجهة InvocationHandler الموجودة في الحزمة java.lang.reflect القدرة على إنشاء فئات وكيل ديناميكية.
مثال الوكيل الديناميكي:
1.BookFacade.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.dao؛
الواجهة العامة BookFacade {
public void addBook();
}
2.BookFacadeImpl.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.dao.impl؛
import net.battier.dao.BookFacade;
فئة عامة BookFacadeImpl تنفذ BookFacade {
@تجاوز
public void addBook() {
System.out.println("إضافة طريقة كتاب...");
}
}
BookFacadeProxy.java
الحزمة net.battier.proxy؛
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* فئة وكيل الوكيل الديناميكي JDK
*
* @الطالب المؤلف
*
*/
الطبقة العامة BookFacadeProxy تنفذ InvocationHandler {
هدف كائن خاص؛
/**
* ربط كائن المفوض وإرجاع فئة الوكيل
* @param الهدف
* @يعود
*/
ربط الكائن العام (الهدف الهدف) {
this.target = target;
// احصل على كائن الوكيل
إرجاع Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this; // لربط الواجهة (هذا عيب، cglib يعوض هذا الخلل)
}
@تجاوز
/**
* طريقة الاتصال
*/
استدعاء الكائن العام (وكيل الكائن، طريقة الطريقة، وسيطة الكائن [])
رميات قابلة للرمي {
نتيجة الكائن = فارغة؛
System.out.println("يبدأ الشيء");
// طريقة التنفيذ
result=method.invoc(target, args);
System.out.println("نهاية الأشياء");
نتيجة الإرجاع؛
}
}
3.TestProxy.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.test؛
import net.battier.dao.BookFacade;
import net.battier.dao.impl.BookFacadeImpl;
import net.battier.proxy.BookFacadeProxy;
الطبقة العامة TestProxy {
public static void main(String[] args) {
وكيل BookFacadeProxy = BookFacadeProxy() الجديد;
BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
bookProxy.addBook();
}
}
ومع ذلك، يعتمد الوكيل الديناميكي لـ JDK على تنفيذ الواجهة. إذا لم تقم بعض الفئات بتنفيذ الواجهة، فلا يمكن استخدام وكيل JDK، لذلك يجب استخدام الوكيل الديناميكي cglib.
Cglib الوكيل الديناميكي
يمكن لآلية الوكيل الديناميكي لـ JDK أن تقوم فقط بتوكيل الفئات التي تنفذ الواجهات، ولا يمكن للفئات التي لا يمكنها تنفيذ الواجهات تنفيذ الوكيل الديناميكي لـ JDK، حيث يقوم cglib على إنشاء فئة فرعية للفئة المستهدفة المحددة وتجاوز الطرق لتحقيق التحسين ، ولكن نظرًا لاستخدام الميراث، لا يمكن تمثيل الفئة المعدلة نهائيًا.
مثال
1.BookFacadeCglib.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.dao؛
الواجهة العامة BookFacade {
public void addBook();
}
2.BookCadeImpl1.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.dao.impl؛
/**
* هذه فئة تنفيذ لا تقوم بتنفيذ الواجهة
*
* @الطالب المؤلف
*
*/
الطبقة العامة BookFacadeImpl1 {
public void addBook() {
System.out.println("الطريقة الشائعة لإضافة الكتب...");
}
}
3.BookFacadeProxy.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.proxy؛
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* استخدم الوكيل الديناميكي cglib
*
* @الطالب المؤلف
*
*/
فئة عامة BookFacadeCglib تنفذ MethodInterceptor {
هدف كائن خاص؛
/**
* إنشاء كائن الوكيل
*
* @param الهدف
* @يعود
*/
كائن عام getInstance (هدف الكائن) {
this.target = target;
محسن محسن = محسن جديد ()؛
Enhancer.setSuperclass(this.target.getClass());
// طريقة رد الاتصال
Enhancer.setCallback(this);
// إنشاء كائن وكيل
إرجاع مُحسِّن.إنشاء () ؛
}
@تجاوز
// طريقة رد الاتصال
اعتراض الكائن العام (Object obj، طريقة الطريقة، Object[] args،
وكيل MethodProxy) يلقي Throwable {
System.out.println("يبدأ الشيء");
proxy.invocationSuper(obj, args);
System.out.println("نهاية الأشياء");
عودة فارغة؛
}
}
4.TestCglib.java
انسخ رمز الكود كما يلي:
الحزمة net.battier.test؛
import net.battier.dao.impl.BookFacadeImpl1;
import net.battier.proxy.BookFacadeCglib;
الطبقة العامة TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib=new BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}