خطافات دورة حياة الحبة
خطافات دورة حياة الحبة
يُدير Spring كل حبة (bean) منذ لحظة إنشائها حتى لحظة إيقاف تشغيل سياق التطبيق. على امتداد هذه الرحلة يوفر الحاوي نقطتي ربط محددتين تنفيذٍ يمكن لكودك من خلالهما أن يعمل: فور اكتمال البناء وحقن التبعيات (ما بعد البناء)، وقُبيل إزالة الحبة من السياق (ما قبل التدمير). إنّ معرفة كيفية استخدام هذه الخطافات بشكل صحيح — وأي واجهة برمجية تختار — أمرٌ ضروري لكتابة حبات تبدأ بشكل موثوق وتُنظّف أثرها عند الانتهاء.
لماذا تهمّ خطافات دورة الحياة
تخيّل حبةً تُغلّف تجمّع اتصالات بقاعدة البيانات، أو تفتح مقبضَ ملف، أو يجب أن تُسجّل نفسها مع خدمة خارجية عند الإطلاق. لا يصحّ تنفيذ أيٍّ من ذلك داخل المُنشئ (constructor) — ففي تلك اللحظة لم يحقن Spring تبعيات الحبة بعد. يجب أن يظل المُنشئ خفيف الوزن وخاليًا من الآثار الجانبية. يمنحك @PostConstruct لحظةً آمنةً مضمونةً لتشغيل التهيئة بعد توصيل جميع التبعيات.
الحاجة المقابلة موجودة عند الإغلاق: يجب استنزاف تجمّعات الاتصالات، وإغلاق المقابس، وإلغاء تسجيل التسجيلات الخارجية. يمنحك @PreDestroy لحظةً يمكن التنبؤ بها للقيام بكل ذلك قبل خروج JVM.
@PostConstruct — التهيئة بعد التوصيل
@PostConstruct هو تعليق توضيحي من Jakarta EE (في حزمة jakarta.annotation) تدعمه Spring دون إعداد إضافي. يُوضع على أي دالة عامة أو محمية أو على مستوى الحزمة، لا تأخذ وسيطات وتُعيد void. تستدعي Spring تلك الدالة مرة واحدة فورًا بعد الانتهاء من حقن جميع التبعيات.
outputDir داخل المُنشئ ستجدها null — تحقن Spring قيم الحقول بعد انتهاء المُنشئ. @PostConstruct هي أبكر لحظة آمنة لاستخدام أي قيمة محقونة.
@PreDestroy — التنظيف قبل الإيقاف
تُعلّم @PreDestroy، من الحزمة jakarta.annotation ذاتها، دالةً ستستدعيها Spring قُبيل تدمير الحبة — عادةً أثناء ApplicationContext.close() أو عند إغلاق JVM إذا سجّل السياق خطافَ إغلاق. تنطبق نفس قواعد التوقيع: لا وسيطات، ونوع إعادة void.
InitializingBean و DisposableBean — أسلوب الواجهة
قبل أن تصبح @PostConstruct و@PreDestroy سائدتين، كانت Spring توفر نقطتَي الربط ذاتيهما عبر واجهتين في org.springframework.beans.factory:
InitializingBean— تُعلن عنafterPropertiesSet()التي تُستدعى بعد اكتمال الحقن.DisposableBean— تُعلن عنdestroy()التي تُستدعى قبل تدمير الحبة.
التعليق التوضيحي أم الواجهة: أيهما تختار؟
الآليتان متكافئتان وظيفيًا عند استخدامهما على حبات مفردة (singleton). يكمن الفرق العملي في مستوى الترابط:
- افضّل
@PostConstruct/@PreDestroyفي كل مشروع جديد تقريبًا. هذه تعليقات توضيحية قياسية من Jakarta EE — تُترجم حبتك وتعمل حتى دون Spring في مسار الفئات، مما يُسهّل اختبارها بمعزل. - استخدم الواجهات فقط عند كتابة كود البنية التحتية على مستوى الإطار الذي يستهدف Spring API عمدًا، أو عند صيانة كود قديم يستخدمهما بالفعل.
default-init-method) وخصائص init-method / destroy-method لكل حبة في تعريفات @Bean. لدالة @Bean يمكنك كتابة @Bean(initMethod = "start", destroyMethod = "stop") — مفيد عندما لا تملك الفئة ولا يمكنك إضافة تعليقات توضيحية إليها (مثل مكتبة طرف ثالث).
ترتيب التنفيذ الدقيق
عندما تكون الآليات الثلاث موجودة على نفس الحبة تستدعيها Spring بترتيب محدد:
- المُنشئ (Constructor)
- حقن التبعيات (الحقول والمُعيِّنات)
- دالة
@PostConstruct InitializingBean.afterPropertiesSet()- دالة
@Bean(initMethod = ...)
عند الإغلاق ينعكس الترتيب لخطافات التدمير:
- دالة
@PreDestroy DisposableBean.destroy()- دالة
@Bean(destroyMethod = ...)
نادرًا ما تخلط الآليات على حبة واحدة في الممارسة، لكن فهم الترتيب يُساعد عند وراثة فئة أساسية تُطبّق InitializingBean بالفعل وتريد إضافة @PostConstruct في فئة فرعية.
تسجيل خطاف الإغلاق
في تطبيقات Spring المستقلة (غير العاملة في حاوي servlet) يجب عليك تسجيل خطاف إغلاق JVM صراحةً حتى تُشغَّل استدعاءات @PreDestroy عند خروج العملية. أبسط طريقة هي استخدام ConfigurableApplicationContext:
تفعل Spring Boot ذلك تلقائيًا. في سياق Spring العادي أنت مسؤول عن استدعاء registerShutdownHook() أو استدعاء ctx.close() صراحةً.
نمط عملي: تدفئة ذاكرة التخزين المؤقت
تتمثّل إحدى حالات الاستخدام الكلاسيكية لـ @PostConstruct في التحميل المسبق للبيانات كي لا يتحمّل الطلب الأول تكلفة الإطلاق البارد:
الخلاصة
@PostConstruct و@PreDestroy هما خطافا دورة الحياة القياسيان والمحمولان لحبات Spring. يمنحانك وصولًا آمنًا موقوتًا بدقة لتشغيل التهيئة بعد الحقن والتنظيف قبل التدمير. واجهتا InitializingBean / DisposableBean سابقتان للتعليقات التوضيحية ولا تزالان صالحتين لكنهما تربطان حبتك بـ Spring API. للفئات التابعة لطرف ثالث التي لا يمكنك توصيف تعليقاتها، استخدم خصائص initMethod / destroyMethod الخاصة بـ @Bean. دائمًا سجّل خطاف الإغلاق في التطبيقات المستقلة. في الدرس القادم ستكتشف كيف يُتيح لك التهيئة الكسولة (lazy initialization) تأجيل هذا التسلسل الكامل حتى تُحتاج الحبة فعلًا.