حاوية Spring وApplicationContext
حاوية Spring وApplicationContext
في جوهره، Spring هو حاوية (Container) — بيئة تشغيل تُنشئ الكائنات وتربط تبعياتها وتدير دورة حياتها وتُتلفها حين لا تعود مطلوبة. إنّ فهم ماهية الحاوية، وأي واجهة تستخدم، وكيفية تشغيلها هو أول مهارة عملية يجب على كل مطوّر Spring أن يمتلكها.
نكهتان: BeanFactory مقابل ApplicationContext
يُوفّر Spring واجهتَي حاوية رئيسيتَين. BeanFactory (حزمة org.springframework.beans.factory) هي أدنى واجهة مستوى: تستطيع إنشاء الـ beans وحقن التبعيات فحسب. ApplicationContext (حزمة org.springframework.context) تمتد من BeanFactory وتضيف كل ما يحتاجه تطبيق حقيقي:
- الإعداد بالتعليقات التوضيحية (Annotations) — تقرأ
@Componentو@Beanو@Autowiredوغيرها. - نشر الأحداث —
ApplicationEventPublisher/ApplicationListener - التدويل (i18n) —
MessageSourceللرسائل متعددة اللغات - تحميل الموارد — تجريد
Resourceيدعم classpath والملفات وعناوين URL - تكامل AOP — نسج الجوانب القائم على الـ proxy تُشغّله الحاوية
BeanFactory موجودة أساسًا لأجزاء الإطار الداخلية والبيئات المحدودة الموارد. كل صنف حاوية ملموس ستصادفه — AnnotationConfigApplicationContext وسياق Spring Boot المدمج وغيرها — هو تنفيذ لـ ApplicationContext.
التنفيذات الملموسة التي ستستخدمها فعلًا
أكثر ثلاثة تنفيذات لـ ApplicationContext شيوعًا هي:
AnnotationConfigApplicationContext— يُشغّل الحاوية من فئات Java المزيّنة بـ@Configurationو/أو حزم مسح المكوّنات. هذا هو الخيار المعياري في تطبيقات Spring 6 الحديثة.ClassPathXmlApplicationContext— يحمّل تعريفات الـ beans من ملفات XML في مسار الفئات. لا يزال يظهر في المشاريع القديمة لكنّه غير موصى به في الكود الجديد.GenericWebApplicationContext/AnnotationConfigWebApplicationContext— يُستخدَم داخل حاوية Servlet (Tomcat، Jetty) أو بشكل ضمني من قِبل خادم Spring Boot المدمج.
التشغيل باستخدام AnnotationConfigApplicationContext
إنشاء حاوية من فئة @Configuration لا يحتاج سوى ثلاثة أسطر من الكود. تقوم الحاوية بما يلي:
- قراءة فئة الإعداد وجميع الـ beans التي تُعلن عنها.
- تنفيذ مسح المكوّنات إن وُجد
@ComponentScan. - إنشاء كل singleton bean وحقن تبعياته وتشغيل callbacks الخاصة بـ
@PostConstruct.
AnnotationConfigApplicationContext الواجهة Closeable، فكتابة try (var ctx = new AnnotationConfigApplicationContext(AppConfig.class)) { ... } يضمن استدعاء ctx.close() وتدمير الـ beans بشكل نظيف.
ما الذي يحدث أثناء التحديث (Refresh)
الحاوية ليست مجرد HashMap مزيّن. حين تستدعي المُنشئ (أو refresh() صراحةً)، يُنفّذ Spring دورة حياة محدّدة جيدًا:
- تحليل الإعداد — يقرأ فئات
@Configurationوملفات XML أو نتائج مسح المكوّنات، ويبني سجلًا داخليًا من كائناتBeanDefinition. - تطبيق BeanFactoryPostProcessors — نقاط تمديد يمكنها تعديل تعريفات الـ beans قبل إنشاء أي bean (مثل
PropertySourcesPlaceholderConfigurerالتي تحلّ عناصر${...}). - إنشاء الـ singletons — إنشاء جميع الـ beans الغير كسولة، وحقن تبعياتها، واستدعاء
@PostConstruct/InitializingBean.afterPropertiesSet(). - نشر ContextRefreshedEvent — إشارة إلى أن الحاوية جاهزة تمامًا.
BeanCurrentlyInCreationException عند بدء التشغيل — لا عند وقت التشغيل. هذا مقصود: يُجبرك على إعادة تصميم العلاقة (أدخل وسيطًا ثالثًا أو انتقل إلى حقن setter أو حقل لأحد الطرفين).
استرجاع الـ Beans من الحاوية
يمكنك سحب الـ beans من الحاوية بعدة طرق، غير أنك في تطبيق Spring مُصمَّم جيدًا لن تفعل ذلك تقريبًا خارج الطريقة main أو الاختبارات — الحاوية تحقن الـ beans لك في كل مكان آخر.
ctx.getBean(...) داخل خدمة أو controller يُقرّن منطق عملك بـ Spring API ويُصعّب اختبار الوحدة. احصر استخدامه في نقطة دخول التطبيق (main) وفي إعداد اختبارات التكامل.
تسلسل الحاوية الهرمي (السياق الأب / الطفل)
يدعم Spring السياقات المتداخلة: يرى سياق الطفل جميع الـ beans من سياقه الأب، بينما لا يرى الأب beans الطفل. كانت تطبيقات Spring MVC تقليديًا تستخدم هذا النمط — يحمل السياق الجذر الخدمات والمستودعات، بينما يحمل سياق المُوزّع (dispatcher) المتحكمات ومحلّلات العرض. مع Spring Boot يصبح هذا التسلسل الهرمي شفّافًا في معظمه، لكن فهمه يُفسّر رسائل الخطأ مثل "No qualifying bean of type X found" التي تظهر حين يُعرَّف bean في مستوى السياق الخاطئ.
إغلاق الحاوية بشكل صحيح
إغلاق الحاوية يُشغّل دورة حياة التدمير: تُنفَّذ callbacks الخاصة بـ @PreDestroy، وتعمل DisposableBean.destroy()، وتُحرَّر الموارد (تجمّعات الخيوط، تجمّعات الاتصالات، مقابض الملفات). في التطبيقات المستقلة، سجّل خطّاف إيقاف JVM حتى يحدث هذا حتى عند الضغط على CTRL-C:
يستدعي Spring Boot التابع registerShutdownHook() تلقائيًا — شيء أقل لتنساه في خدمات الإنتاج.
الخلاصة
ApplicationContext هي حاوية Spring التي ستتعامل معها يوميًا. تقرأ إعدادك (Java أو annotations أو XML)، وتُنشئ جميع الـ beans وتربطها خلال مرحلة تحديث مُحدَّدة جيدًا، وتُتلفها بشكل صحيح عند الإغلاق. AnnotationConfigApplicationContext هو الصنف الملموس الذي تلجأ إليه في تطبيقات Spring الحديثة غير المبنية على Boot. إنّ فهم تسلسل التشغيل هذا — تحليل، معالجة لاحقة، إنشاء، نشر حدث الجاهزية — هو النموذج الذهني الذي يُسرّع كل جلسة تصحيح أخطاء في Spring.