المفاهيم الأساسية لـ AOP
المفاهيم الأساسية لـ AOP
يُقدّم البرمجة الموجّهة نحو الجوانب (AOP) مفردات دقيقة ومحددة. كل نقاش حول AOP — كل مُرشّح (annotation) في Spring، كل أثر في المُنقّح — يستخدم هذه المصطلحات الخمسة: الجانب (Aspect)، ونقطة الالتحاق (Join Point)، والنصيحة (Advice)، ونقطة القطع (Pointcut)، والنسيج (Weaving). أتقنها الآن وسيغدو باقي البرنامج التعليمي سهلًا. أُهملها وستصبح كل شيء ضبابيًا.
الجانب (Aspect)
الجانب هو الوحدة المعيارية التي تُغلّف الاهتمام المشترك بين الأجزاء المختلفة. فكّر فيه على أنه مكافئ AOP للصنف (class): فهو يجمع النصائح وتعريفات نقاط القطع معًا، تمامًا كما يجمع الصنف الحقول والتوابع ذات الصلة.
في Spring تُعلن الجانب بمُرشّحَين على وحدة Spring عادية:
بدون @Component (أو ما يعادله)، لن تُنشئ Spring نسخة من الصنف أبدًا ولن يكون للجانب أي أثر. وبدون @Aspect، ستتجاهل Spring مُرشّحات النصيحة داخله. كلا المُرشّحَين ضروريان.
نقطة الالتحاق (Join Point)
نقطة الالتحاق هي أي نقطة محددة في تنفيذ برنامجك حيث يمكن تطبيق النصيحة. تدعم Spring AOP نوعًا واحدًا فقط من نقاط الالتحاق: تنفيذ التابع (method execution). في كل مرة يُستدعى فيها تابع وحدة Spring المُدارة، يُعدّ ذلك الاستدعاء نقطة التحاق.
هذا هو القيد الرئيسي في Spring AOP مقارنةً بـ AspectJ الكامل: لا يمكنك اعتراض قراءة الحقول، أو إنشاء الكائنات، أو استدعاء التوابع الساكنة في Spring AOP الخالص. من الناحية العملية، يغطي تنفيذ التابع 95% من احتياجات القطع المشتركة الحقيقية (التسجيل، فحوصات الأمان، المعاملات، التخزين المؤقت)، لذا نادرًا ما يؤذي هذا القيد.
عند التشغيل، حين تُطلَق النصيحة، تُغلّف Spring نقطة الالتحاق في كائن JoinPoint (من الواجهة org.aspectj.lang.JoinPoint) يمكنك فحصه:
النصيحة (Advice)
النصيحة هي الكود الذي يعمل فعلًا عند نقطة الالتحاق — إنها الماذا والمتى للاعتراض. تدعم Spring AOP خمسة أنواع من النصائح، كل منها يتوافق مع لحظة مختلفة بالنسبة لتنفيذ التابع:
@Before— يعمل قبل التابع المستهدف. لا يمكنه منع التابع من التشغيل (استخدم@Aroundلذلك).@AfterReturning— يعمل بعد أن يُرجع التابع بشكل طبيعي. يستقبل القيمة المُرجعة.@AfterThrowing— يعمل إذا رمى التابع استثناءً. يستقبل كائن الاستثناء.@After— يعمل بعد انتهاء التابع، سواء بشكل طبيعي أو مع استثناء (مثل كتلةfinally).@Around— يُغلّف الاستدعاء بأكمله؛ تستدعيproceed()لتشغيل التابع الحقيقي (يُغطى بعمق في الدرس 6).
مثال سريع على نصيحة @Before في السياق:
وسيطة السلسلة النصية لـ @Before هي تعبير نقطة قطع — وهو ما يقودنا إلى المفهوم التالي.
نقطة القطع (Pointcut)
نقطة القطع هي محمول (predicate) — تعبير منطقي — يختار مجموعة فرعية من جميع نقاط الالتحاق. حيث تجيب النصيحة على سؤال ماذا أفعل، تجيب نقطة القطع على سؤال أين أفعله. الاثنان مفصولان عن قصد: يمكنك إعادة استخدام نقطة القطع عبر نصائح متعددة، ويمكنك تغيير إحداهما دون المساس بالأخرى.
تستخدم Spring AOP لغة تعبيرات نقطة القطع الخاصة بـ AspectJ. أكثر المحددات شيوعًا هو execution:
الممارسة الموصى بها هي استخراج نقاط القطع المُسمّاة باستخدام @Pointcut لكي يمكن إعادة استخدامها وتركيبها:
@Pointcut لا يُنفَّذ أبدًا فعليًا. إنه مجرد مرساة مُسمّاة يمكن للغة التعبيرات الرجوع إليها. يجب أن يكون التابع موجودًا وأن يُرجع void — الاسم هو ما يهم.
النسيج (Weaving)
النسيج هو عملية ربط الجوانب بالكائنات التي تؤثر عليها — الخطوة الآلية التي تجعل الاعتراض يعمل. هناك ثلاثة أوقات محتملة يمكن أن يحدث فيها النسيج:
- النسيج وقت التحويل (CTW): يُعدّل مُحوّل AspectJ (
ajc) الكود الثنائي (bytecode) أثناء التحويل. ملفات.classالناتجة تحتوي بالفعل على كود النصيحة مُضمَّنًا. الأسرع في وقت التشغيل؛ يتطلب مُحوّل AspectJ في سلسلة البناء. - النسيج وقت التحميل (LTW): وكيل Java يُعيد كتابة الكود الثنائي أثناء تحميل الصفوف بواسطة محمّل الصفوف. لا حاجة لتغيير المُحوّل؛ يُضيف تكلفة بدء التشغيل.
- النسيج وقت التشغيل (قائم على البروكسي): يُنشئ الإطار كائن بروكسي عند البدء يُغلّف الوحدة الحقيقية. هذا ما تستخدمه Spring AOP حصريًا. لا تعديل للكود الثنائي؛ يعمل فقط لوحدات Spring المُدارة؛ محدود بنقاط الالتحاق الخاصة بتنفيذ التابع.
النسيج وقت التشغيل في Spring يعني أن النسيج يحدث مرة واحدة عند بدء سياق التطبيق، لا في كل استدعاء تابع. عندما تُنشئ Spring وحدة تتطابق مع نقطة قطع واحدة على الأقل، تستبدل بهدوء مرجع الوحدة البسيطة ببروكسي. المُستدعون يتعاملون مع البروكسي الذي يُشغّل النصيحة ثم يُفوّض إلى الكائن الحقيقي. من منظور المُستدعي، لا شيء يتغير — لا يزال يحصل على الواجهة أو الصنف الذي طلبه.
this.someMethod())، يتجاوز الاستدعاء البروكسي تمامًا ولا تُطلَق أي نصيحة. هذا هو الخطأ الأكثر شيوعًا في Spring AOP. الحل هو حقن الوحدة في نفسها عبر @Autowired على حقل، أو استخدام نسيج AspectJ الكامل. يُغطّي الدرس 9 هذا بالتفصيل.
الجمع في صورة واحدة
إليك سيناريو محدد يستخدم المفاهيم الخمسة دفعةً واحدة. المتطلب: تسجيل كل استدعاء تابع عام في الحزمة com.example.service.
الخلاصة
تُشكّل المفاهيم الخمسة لـ AOP سلسلة ذهنية واضحة: الجانب يجمع منطق القطع المشترك ذا الصلة؛ نقطة الالتحاق هي أي نقطة اعتراض مؤهلة (في Spring AOP: أي استدعاء تابع لوحدة Spring)؛ النصيحة هي الكود الذي يعمل عند نقطة الالتحاق وتُحدد متى يعمل (قبل، بعد، حول)؛ نقطة القطع هي التعبير الذي يختار أي نقاط الالتحاق تُطلق أي نصيحة؛ والنسيج هي العملية — في حالة Spring: إنشاء البروكسي عند البدء — التي تربط الجوانب بالتطبيق الجاري. الدرس القادم يحوّل هذه المفاهيم إلى كود عامل بأول جانب كامل لك.