لتوكيل فئة باستخدام وكيل ديناميكي jdk، يجب أن تقوم فئة الوكيل بتنفيذ واجهة واحدة على الأقل، ويمكن إنشاء وكيل فقط للطرق الموجودة في الواجهة.
ينقسم تنفيذ الوكيل الديناميكي في jdk بشكل عام إلى ثلاث خطوات:
1. كتابة الواجهات وفئات التنفيذ.
2. اكتب معالجًا ينفذ واجهة InvocationHandler. تحتوي هذه الواجهة على طريقة واحدة فقط، وتوقيعها هو استدعاء الكائن العام (Object proxy، Method Method، Object[] args).
throws Throwable؛ يمكنك إضافة التعليمات البرمجية الخاصة بك إلى طريقة تنفيذ المعالج قبل وبعد استدعاء الطريقة لإجراء الاعتراض الديناميكي. تجدر الإشارة إلى أن فئة الوكيل الديناميكية التي تم إنشاؤها بواسطة الوكيل ليست هي الفئة الحقيقية التي نقوم بتوكيلها، لذلك يمكننا إضافة متغير عضو من النوع Object إلى المعالج للإشارة إلى الفئة التي نريد حقًا أن نكون وكيلاً لها (أي، فئة في الخطوة 1 فئة التنفيذ).
3. استخدم الأسلوب newProxyInstance للفئة java.lang.reflect.Proxy لإنشاء فئة وكيل ديناميكية. بالنسبة لجميع استدعاءات أساليب الوكيل، يمكنك الاتصال مباشرة بأسلوب فئة الوكيل الديناميكي الذي تم إنشاؤه، ولكن يجب عليك أولاً إجراء تحويل نوع قسري عليها وتحويلها إلى واجهة الطريقة التي نريد الاتصال بها.
تحليل مبدأ JDK:
من خلال تحليل الكود المصدري للوكيل، يمكنك رؤية الإنشاء التفصيلي لفئات الوكيل الديناميكية. تقوم طريقة newProxyInstance أولاً بإنشاء مثيل فئة لفئة الوكيل الديناميكي، ثم تستدعي مُنشئها الذي يكون نوع معلمته InvocationHandler لإنشاء فئة الوكيل الديناميكي والعودة.
كيفية إنشاء مثيل فئة لفئة الوكيل الديناميكي؟ يتم استخدام فئة ProxyGenerator لإنشاء دفق بايت للفئة لفئة الوكيل الديناميكي وتحميلها في منطقة الطريقة.
عند تحليل عملية إنشاء دفق بايت للفئة، يمكننا أن نرى أنها تستخدم الوكيل كفئة أصل لها لتنفيذ جميع أساليب الواجهة التي سيتم إنشاء وكيل لها. يستدعي نص التنفيذ لكل طريقة بشكل أساسي طريقة استدعاء المعالج.
الكود الرئيسي لعملية إنشاء دفق البايت للفئة هو كما يلي:
انسخ رمز الكود كما يلي:
البايت الخاص [] توليدClassFile ()
{
addProxyMethod(hashCodeMethod, java/lang/Object);
addProxyMethod(equalsMethod, java/lang/Object);
addProxyMethod(toStringMethod, java/lang/Object);
ل(int i = 0; i <faces.length; i++)
{
الطريقة amethod[] = Interfaces[i].getMethods();
for(int k = 0; k < amethod.length; k++)
addProxyMethod(amethod[k], Interfaces[i]);
}
قائمة القائمة؛
for(Iterator iterator = proxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
list = (List)iterator.next();
يحاول
{
methods.add(generateConstructor());
for(Iterator iterator1 = proxyMethods.values().iterator(); iterator1.hasNext();)
{
List list1 = (List)iterator1.next();
Iterator iterator2 = list1.iterator();
بينما (iterator2.hasNext ())
{
ProxyMethod proxymethod = (ProxyMethod)iterator2.next();
field.add(new FieldInfo(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
methods.add(<SPAN style="COLOR: red">proxymethod.generateMethod()</SPAN><SPAN style="COLOR: #000000">);</SPAN>
انسخ رمز الكود كما يلي:
}
}
methods.add(generateStaticInitializer());
}
قبض على (IOException ioexception)
{
رمي خطأ داخلي جديد("استثناء الإدخال/الإخراج غير المتوقع");
}
إذا (methods.size () > 65535)
طرح IllegalArgumentException الجديد("تم تجاوز حد الطريقة");
إذا (الحقول.الحجم () > 65535)
طرح IllegalArgumentException الجديد("تم تجاوز حد الحقل");
cp.getClass(dotToSlash(className));
cp.getClass("java/lang/reflect/Proxy");
ل(int j = 0; j <faces.length; j++)
cp.getClass(dotToSlash(interfaces[j].getName()));
cp.setReadOnly();
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream);
يحاول
{
dataoutputstream.writeInt(-889275714);
dataoutputstream.writeShort(0);
dataoutputstream.writeShort(49);
cp.write(dataoutputstream);
dataoutputstream.writeShort(49);
dataoutputstream.writeShort(cp.getClass(dotToSlash(className)));
dataoutputstream.writeShort(cp.getClass("java/lang/reflect/Proxy"));
dataoutputstream.writeShort(interfaces. length);
ل(int l = 0; l <faces.length; l++)
dataoutputstream.writeShort(cp.getClass(dotToSlash(interfaces[l].getName())));
dataoutputstream.writeShort(fields.size());
معلومات الحقل معلومات الحقل؛
انسخ رمز الكود كما يلي:
// أضف السمات
for(Iterator iterator3 = field.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo)iterator3.next();
// إضافة طريقة
dataoutputstream.writeShort(methods.size());
this.methodInfomethodinfo;
for(Iterator iterator4 =methods.iterator(); iterator4.hasNext();methodinfo.write(dataoutputstream))
methodinfo = (MethodInfo)iterator4.next();
dataoutputstream.writeShort(0);
}
قبض على (IOException ioexception1)
{
رمي خطأ داخلي جديد ("استثناء إدخال / إخراج غير متوقع")؛
}
return bytearrayoutputstream.toByteArray();
}
ملاحظة: الجزء الأحمر في الكود، proxymethod.generateMethod()، ينشئ نص الطريقة لكل طريقة. من خلال النظر إلى الكود المصدري، يمكننا أن نرى أنهم جميعًا يستدعون طريقة الاستدعاء لمعالج التنفيذ لواجهة InvocationHandler.