تسمية الـ Beans والـ Qualifiers والـ Beans المتعددة
تسمية الـ Beans والـ Qualifiers والـ Beans المتعددة
يتيح لك Spring تسجيل عدد لا محدود من الـ beans من النوع ذاته — وهذه ميزة لا عيب. قد يكون لديك bean من نوع DataSource لقاعدة البيانات الرئيسية وآخر لنسخة القراءة، أو اثنان من RestTemplate مضبوطان بأوقات انتهاء مختلفة، أو عدة تنفيذات لواجهة PaymentGateway. يتحوّل التحدي من كيف أسجّل bean؟ إلى كيف يعرف Spring أيّ bean يحقن حين يوجد أكثر من مرشّح؟ هذا الدرس يجيب على هذا السؤال بشكل كامل.
كيف يسمّي Spring الـ Beans تلقائيًا
لكل bean اسم واحد على الأقل. حين تُعلن عن bean يشتق Spring اسمًا افتراضيًا من المصدر:
- @Component (والصور المشتقة منها): اسم الفئة البسيط مع تصغير الحرف الأول.
OrderServiceتصبح"orderService". - طريقة @Bean: اسم الطريقة. طريقة تُسمّى
primaryDataSource()تُسجّل bean باسم"primaryDataSource". - XML <bean>: السمة
id، مع الرجوع إلى اسم مُولَّد إن أُهمل.
يمكنك تجاوز الاسم الافتراضي في أي مكان يُشتق منه اسم:
BeanDefinitionOverrideException (في Spring Boot 2.1+ بشكل افتراضي). اختر دائمًا أسماء فريدة ووصفية.
الحقن بالاسم باستخدام @Qualifier
حين يحلّ Spring تبعية بالنوع ويجد أكثر من bean مطابق، يرمي NoUniqueBeanDefinitionException. الحل المعياري هو @Qualifier الذي يُخبر Spring بالضبط باسم الـ bean المطلوب:
يعمل @Qualifier على معاملات المُنشئ ومعاملات الـ setter وحقول الـ field injection على حدٍّ سواء. يجب أن تطابق قيمة السلسلة تمامًا اسم bean مسجّلًا أو أحد أسمائه البديلة.
@Primary — اختيار الفائز الافتراضي
إن كان bean واحد ينبغي أن يكون الاختيار الافتراضي لغالبية نقاط الحقن، فضعه بـ @Primary. أي نقطة حقن لا تحمل @Qualifier ستستقبل الـ bean الأساسي؛ أما نقاط الحقن التي تحمل @Qualifier فتحصل على ما طلبته بالضبط.
@Primary دون إعطاء beans أسماء واضحة وصفة لفوضى حين يُضاف bean ثالث لاحقًا. سمّ الـ beans دائمًا بشكل صريح واستخدم @Primary حَكَمًا عند التعادل.
تعليقات توضيحية مخصصة للـ Qualifier
نثر سلاسل نصية مثل "replicaDs" عبر عشرات نقاط الحقن هشّ — خطأ إملائي يُترجَم بنجاح لكنه يفشل وقت التشغيل. النهج الاحترافي هو تعليق توضيحي مخصص للـ qualifier:
طبّقه عند تعريف الـ bean وعند نقطة الحقن:
الآن إعادة الهيكلة عملية وقت الترجمة — أعد تسمية التعليق ويحدّث IDE كل الاستخدامات. الأخطاء الإملائية في السلاسل مستحيلة.
حقن جميع الـ Beans من نوع واحد
أحيانًا تريد كل تنفيذ مسجّل لا واحدًا فحسب. Spring يحقن بسعادة List<T> أو Map<String, T> تحتوي على جميع beans من النوع T:
عند حقن Map<String, T> تكون المفاتيح أسماء الـ beans، ما يتيح لك البحث وقت التشغيل بالاسم دون ربط منطق الأعمال بـ Spring APIs:
التحكم في الترتيب داخل المجموعات — @Order و Ordered
حين يملأ Spring قائمة List<T>، لا يُضمن ترتيب العناصر ما لم تحدده. استخدم @Order على فئة الـ bean (أو نفّذ org.springframework.core.Ordered) لتعيين أولوية. القيم الأصغر تأتي أولًا:
@Qualifier أو انتخابات @Primary. إساءة استخدامها لمحاولة "تجاوز" المرشحين الأساسيين خطأ شائع يُفضي إلى أخطاء خفية.
حلّ الـ Beans برمجيًا
في حالات نادرة — الإرسال الديناميكي ومعماريات الإضافات — تحتاج للبحث عن bean بالاسم أو النوع وقت التشغيل. احقن ApplicationContext واستدع getBean():
يُسمّى هذا النمط Service Locator. مقبول عند حواف المعمارية (مثلًا حين يأتي اسم الـ bean من صف قاعدة بيانات أو طلب مستخدم)، لكن استخدامه في وسط منطق الأعمال يُقيّد كودك بـ Spring ويُصعّب اختبار الوحدات. افضّل حقن المجموعة كلّما كانت جميع المتغيرات معروفة وقت بدء التشغيل.
دليل اتخاذ القرار
- افتراضي واضح مع استثناءات نادرة: استخدم
@Primary+@Qualifierفي المواقع الاستثنائية القليلة. - اثنان أو أكثر من beans متكافئة الأهمية: أعطِ كلًّا منها اسمًا واضحًا أو تعليقًا توضيحيًا مخصصًا؛ بلا
@Primary. - الإرسال لجميع التنفيذات: احقن
List<T>أوMap<String, T>. - الإرسال الديناميكي وقت التشغيل: احقن
Map<String, T>أو الجأ إلىApplicationContext.getBean(). - إعادة هيكلة آمنة وقت الترجمة على نطاق واسع: استبدل qualifiers النصية بتعليقات توضيحية مخصصة.
الخلاصة
يشتق Spring أسماء الـ beans تلقائيًا لكنه يتيح لك تجاوزها في كل مكان. حين يوجد عدة beans من النوع ذاته، يُثبّت @Qualifier الحقن على اسم محدد، ويُنتخب @Primary فائزًا افتراضيًا للمواقع غير المؤهَّلة. تُنقل التعليقات التوضيحية المخصصة للـ qualifier عملية التحديد من سلاسل هشة إلى نظام الأنواع. في سيناريوهات التوزيع الكامل، يُعدّ حقن List<T> أو Map<String, T> النهجَ الأنظف، مع @Order للتحكم في ترتيب القائمة. مجتمعةً تُتيح لك هذه الأدوات إدارة أي عدد من مرشحي الـ bean دون غموض ودون ربط منطق أعمالك بمكونات Spring الداخلية.