0Pricing
Java Academy · درس

الوكلاء الديناميكيون باستخدام InvocationHandler

أنشئ وكلاء JDK ديناميكيين لاعتراض استدعاءات أساليب الواجهات من أجل التسجيل والتخزين المؤقت وإعادة المحاولة

الوكلاء الديناميكيون باستخدام InvocationHandler درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

ما الوكيل الديناميكي؟

الوكيل الديناميكي في JDK هو كائن يُنشأ أثناء التشغيل ويطبّق واجهة واحدة أو أكثر. وتُمرّر جميع استدعاءات الأساليب إلى InvocationHandler الذي توفّره.

إنشاء وكيل باستخدام Proxy.newProxyInstance

استدعِ Proxy.newProxyInstance(classLoader, interfaces[], handler) لإنشاء وكيل. ويطبّق الوكيل جميع الواجهات المدرجة.

UserService proxy = (UserService) Proxy.newProxyInstance(
    UserService.class.getClassLoader(),
    new Class[]{UserService.class},
    new LoggingHandler(realService)
);
proxy.findById(42L); // intercepted by LoggingHandler

واجهة InvocationHandler

طبّق InvocationHandler لمعالجة كل استدعاء لأسلوب. ويتلقى المعالج كائن الوكيل والأسلوب المستدعى والوسائط.

public class LoggingHandler implements InvocationHandler {
    private final Object target;
    public LoggingHandler(Object target) { this.target = target; }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">> " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("<< " + method.getName());
        return result;
    }
}

الوكيل للتخزين المؤقت

يمكن لـ InvocationHandler تخزين النتائج مؤقتًا: فإذا كانت ذاكرة التخزين المؤقت تتضمن نتيجة للوسائط، فأعدها؛ وإلا فاستدعِ الأسلوب الحقيقي وخزّن النتيجة.

public class CachingHandler implements InvocationHandler {
    private final Object target;
    private final Map<String, Object> cache = new HashMap<>();
    public CachingHandler(Object target) { this.target = target; }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String key = method.getName() + Arrays.toString(args);
        return cache.computeIfAbsent(key, k -> {
            try { return method.invoke(target, args); }
            catch (Exception e) { throw new RuntimeException(e); }
        });
    }
}

الوكيل لمنطق إعادة المحاولة

غلّف استدعاءات الأساليب داخل حلقة لإعادة المحاولة. وإذا وقع استثناء، فأعد المحاولة حتى N مرات قبل إعادة إلقاء الاستثناء.

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    for (int attempt = 1; attempt <= 3; attempt++) {
        try { return method.invoke(target, args); }
        catch (InvocationTargetException e) {
            if (attempt == 3) throw e.getCause();
            Thread.sleep(1000L * attempt);
        }
    }
    throw new RuntimeException("unreachable");
}

الوكيل لإدارة المعاملات

يستخدم Spring's @Transactional وكيلًا ديناميكيًا لبدء معاملة قبل استدعاء الأسلوب، ثم تنفيذ commit أو rollback بعده — وكل ذلك من دون علم المستدعي.

التحقق مما إذا كان الكائن وكيلًا

استخدم Proxy.isProxyClass(obj.getClass()) لاكتشاف الوكلاء، وProxy.getInvocationHandler(proxy) لاسترجاع المعالج.

if (Proxy.isProxyClass(service.getClass())) {
    InvocationHandler h = Proxy.getInvocationHandler(service);
    System.out.println("Handler: " + h.getClass().getSimpleName());
}

قيود وكلاء JDK الديناميكيين

تعمل وكلاء JDK الديناميكيون مع الواجهات فقط — ولا يمكنهم إنشاء وكلاء للأصناف الملموسة. ولإنشاء وكيل لصنف، استخدم CGLIB، الذي يستخدمه Spring أيضًا داخليًا لأصناف @Configuration.

CGLIB كبديل

ينشئ CGLIB صنفًا فرعيًا من الصنف المستهدف أثناء التشغيل، مع تجاوز جميع الأساليب غير النهائية. ويستخدمه Spring عند عدم وجود واجهات. ولا يتطلب أي تعليق توضيحي — فهو شفاف.

الوكيل في Hibernate

يحمّل Hibernate ارتباطات الكيانات تحميلًا كسولًا باستخدام وكلاء CGLIB/Byte Buddy. وعند الوصول إلى مجموعة محمّلة تحميلًا كسولًا، ينفّذ الوكيل استعلام SQL.

// Hibernate creates a proxy for:
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
// Accessing dept triggers a DB query via the proxy

أداء الوكلاء

يكون توزيع الأساليب عبر الوكيل الديناميكي أبطأ بنحو 5 إلى 10 مرات من الاستدعاء المباشر. وفي المسارات عالية الاستخدام جدًا، ضع في اعتبارك توليد الكود وقت الترجمة (APT) أو استخدام MethodHandles بدلًا من الوكلاء الديناميكيين.

تحقق سريع

ما القيد الرئيسي لوكلاء JDK الديناميكيين؟

مراجعة

تعترض وكلاء JDK الديناميكيون استدعاءات أساليب الواجهات عبر InvocationHandler. استخدمها للتسجيل والتخزين المؤقت وإعادة المحاولة وإدارة المعاملات. ولإنشاء وكلاء للأصناف، استخدم CGLIB. خزّن إنشاء الوكلاء مؤقتًا، لأنه يعتمد على الانعكاس وليس مجانيًا.

الأسئلة الشائعة

هل درس «الوكلاء الديناميكيون باستخدام InvocationHandler» مجاني؟

نعم — نص درس «الوكلاء الديناميكيون باستخدام InvocationHandler» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «الوكلاء الديناميكيون باستخدام InvocationHandler»؟

أنشئ وكلاء JDK ديناميكيين لاعتراض استدعاءات أساليب الواجهات من أجل التسجيل والتخزين المؤقت وإعادة المحاولة تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «الوكلاء الديناميكيون باستخدام InvocationHandler»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. فحص الفئات باستخدام Reflection
  2. استدعاء الأساليب والوصول إلى الحقول
  3. الوكلاء الديناميكيون باستخدام InvocationHandler
  4. أداء Reflection والبدائل
← العودة إلى Java Academy