الـ Beans وتعريفاتها
الـ Beans وتعريفاتها
في Spring، أي كائن تديره حاوية IoC يُسمّى bean. هذا هو التعريف بأكمله — غير أنّ تداعياته عميقة. حين تُسلّم كائنًا إلى Spring ليديره، تمتلك Spring دورة حياته الكاملة: تُنشئه، وتصل تبعياته، وتطبّق أي معالجة لاحقة عليه، ثم تُتلفه عند إغلاق التطبيق. إنّ فهم ما يجعل شيئًا ما bean، وكيف تُوصَف الـ beans للحاوية، وما يحدث لها في وقت التشغيل هو أساس كل تطبيق Spring ستكتبه.
ما هو الـ Bean تحديدًا؟
الـ bean هو ببساطة كائن Java تُنشئه ApplicationContext الخاصة بـ Spring وتضبطه وتديره. لا يوجد شيء خاص في الفئة نفسها — لا تحتاج إلى تنفيذ واجهة Spring أو توسيع فئة أساسية فيها. UserRepository أو EmailService أو Clock مخصص — أي منها يمكن أن يكون bean. الفرق بين الـ bean والكائن العادي هو من يُنشئه: Spring تفعل ذلك، لا كودك أنت.
تعريف الـ Bean — مخطط Spring
قبل أن تُنشئ Spring bean، تحتاج إلى تعريف bean: سجل بيانات وصفية يصف فئة الـ bean ونطاقه وتبعياته وأي دوال تهيئة أو إتلاف وتفاصيل تهيئة أخرى. نادرًا ما تتعامل مع تعريفات الـ bean مباشرةً — فأنت تُعبّر عنها عبر التعليقات التوضيحية (annotations) أو إعداد Java، وتُترجمها Spring داخليًا إلى كائنات BeanDefinition. لكن معرفة المعلومات التي يحتوي عليها تعريف الـ bean يساعدك على فهم كل خيار إعداد تعرضه Spring.
يلتقط تعريف الـ bean:
- الفئة — فئة Java التي يجب إنشاؤها.
- الاسم / المعرّف — المعرّف المستخدم للبحث عن الـ bean.
- النطاق —
singleton(نسخة واحدة مشتركة، وهو الافتراضي) أوprototype(نسخة جديدة عند كل طلب)، من بين أنواع أخرى. - وسيطات المُنشئ وقيم الخصائص — ما يجب حقنه.
- استدعاءات التهيئة والإتلاف — الدوال التي تُستدعى بعد الربط وقبل الإغلاق.
- علامة التهيئة الكسولة — هل تُنشئ الـ bean فور بدء تشغيل السياق أم عند أول استخدام فقط.
تعريف Beans باستخدام @Bean داخل فئة @Configuration
الطريقة الأكثر صراحةً لتعريف الـ bean هي دالة @Bean داخل فئة @Configuration. تستدعي Spring هذه الدالة مرة واحدة (للـ beans ذات نطاق singleton)، وتخزّن الكائن المُعاد، وتسجّله باسم الدالة كاسم للـ bean.
userService() دالةَ userRepository()، قد تتوقع إنشاء JdbcUserRepository ثانٍ. لكن هذا لا يحدث. تُنشئ Spring فئةً فرعية من فئة @Configuration باستخدام CGLIB وتعترض استدعاءات الدوال بين الـ beans، مُعيدةً كائن الـ singleton المُنشأ بالفعل. لهذا يجب ألا تكون فئات @Configuration نهائية (final).
تعريف Beans باستخدام @Component (مدفوع بالتعليقات التوضيحية)
لفئاتك الخاصة، يكون وسم الفئة بـ @Component (أو أحد تصنيفاتها الفرعية) وتفعيل فحص المكونات عادةً أكثر إيجازًا. تفحص Spring الحزم المحددة، وتجد الفئات المُوسَمة، وتُسجّل تعريف bean لكل منها تلقائيًا.
مع تفعيل فحص المكونات (يُغطّى بالتفصيل في الدرس السادس)، تُصبح الفئتان أعلاه beans تلقائيًا. يكتشف الحاوية نمط المُنشئ الفردي ويحقن التبعية المتطابقة دون أي تعليمات ربط صريحة.
نطاق الـ Bean: Singleton مقابل Prototype
يتحكم النطاق في عدد النسخ التي تحتفظ بها الحاوية ومتى تُنشأ.
- Singleton (الافتراضي): نسخة مشتركة واحدة لكل حاوية. تُنشأ عند بدء التشغيل (ما لم تكن كسولة)، وتُعاد استخدامها لكل نقطة حقن. مناسبة للخدمات عديمة الحالة والمستودعات ومعظم مكونات التطبيق.
- Prototype: تُنشأ نسخة جديدة في كل مرة يُطلب فيها الـ bean أو يُحقَن. مناسبة للكائنات ذات الحالة (stateful) وغير الآمنة للخيوط التي يجب ألا تُشارَك.
ApplicationContext.getBean() أو Provider<ReportBuilder> لطلب نسخة جديدة في كل مرة.
استدعاءات دورة الحياة: @PostConstruct و@PreDestroy
يمكن لـ Spring استدعاء دوال على الـ bean بعد حقن جميع التبعيات وقبل إغلاق السياق. هذه هي الخطاطيف القياسية لتهيئة الموارد (فتح تجمّع اتصالات، تسخين ذاكرة تخزين مؤقت) وتحريرها (إغلاق الاتصالات، تفريغ المخازن المؤقتة).
javax.annotation.* إلى jakarta.annotation.*. استخدم دائمًا استيراد jakarta في المشاريع الجديدة.
التهيئة الكسولة
بشكل افتراضي، تُنشأ الـ beans ذات نطاق singleton بحرص — عند بدء تشغيل السياق. يكتشف هذا أخطاء الإعداد فورًا (تبعية bean مفقودة تفشل بسرعة). في بعض الأحيان تريد إنشاء الـ bean عند أول استخدام فقط، مثلًا إذا كانت تتصل بخدمة خارجية قد لا تكون متاحة عند بدء التشغيل. أضف @Lazy:
في التطبيقات الكبيرة يمكنك جعل الكسل افتراضيًا لجميع الـ beans عبر @ComponentScan(lazyInit = true) أو spring.main.lazy-initialization=true في application.properties، مما يُحسّن وقت البدء على حساب تأجيل الأخطاء من وقت البدء إلى وقت الاستخدام الأول.
الخلاصة
الـ bean في Spring هو أي كائن تُدير دورة حياته حاوية IoC. يستند كل bean إلى تعريف bean — مخطط يُحدد الفئة والنطاق والتبعيات واستدعاءات دورة الحياة. تُعبّر عن تعريفات الـ bean إما صراحةً عبر دوال @Bean في فئات @Configuration، أو ضمنيًا بوسم فئاتك الخاصة بـ @Component (أو تصنيف فرعي) وتفعيل فحص المكونات. نطاق singleton الافتراضي صحيح للمتعاونين عديمي الحالة؛ ونطاق prototype موجود للكائنات ذات الحالة بطبيعتها. تمنحك خطاطيف دورة الحياة (@PostConstruct، @PreDestroy) نقاط دخول وخروج نظيفة لإدارة الموارد. كل ميزة أخرى في Spring — حقن التبعيات، والبرمجة الموجهة بالجوانب (AOP)، والمعاملات — تبني على نموذج الـ bean هذا.