We are still cooking the magic in the way!
الإعداد القائم على Java
الإعداد القائم على Java
دعم Spring طرقًا متعددة لتعريف الـ beans عبر تاريخه. جاء أسلوب XML أولًا، ثم التعليقات التوضيحية كـ @Component و@Autowired، وأخيرًا — مع Spring 3.0 ومع تحسينات كبيرة في Spring 4 وما بعده — الإعداد القائم على Java: فئات Java عادية مُزيَّنة بـ @Configuration تحتوي على توابع مُزيَّنة بـ @Bean تُعرِّف الـ beans وتربطها في الكود. في تطبيق Spring Boot 3 اليوم، هذا هو الأسلوب السائد الذي تحتاج إلى إتقانه بالكامل.
فئة @Configuration
فئة مُزيَّنة بـ @Configuration تُخبر Spring: "عامل هذه الفئة كمصدر لتعريفات الـ beans." يُنشئ Spring وكيل CGLIB لتلك الفئة حتى يستطيع اعتراض استدعاءات توابع @Bean وتطبيق عقد Singleton (موضَّح أدناه). في أبسط صورته:
يستدعي Spring التابع passwordEncoder() مرةً واحدةً، يحفظ النتيجة في سياق التطبيق تحت اسم الـ bean الافتراضي passwordEncoder (اسم التابع)، ويُعيد نفس الكائن لأي مكوّن يحتاج إلى حقن PasswordEncoder.
@Bean("encoder") أو @Bean(name = {"encoder", "pwdEncoder"}) (أسماء بديلة). الاسم الأول هو الأساسي وما بعده مجرد أسماء مستعارة.
تعريف التبعيات بين الـ Beans
الطريقة الأكثر وضوحًا للتعبير عن أن bean أ يعتمد على bean ب هي تعريف bean ب كمعامل في تابع @Bean الخاص بـ أ. يحلّ Spring ذلك حسب النوع من السياق، تمامًا كما يفعل مع حقن المُنشئ:
هذا الأسلوب يجعل رسم التبعيات صريحًا ومرئيًا وقت الترجمة. يمكنك رؤية ما يحتاجه كل bean دفعةً واحدة دون الغوص في مُنشئ الفئة.
وكيل CGLIB وضمان Singleton
تأمّل ما يحدث عندما يستدعي تابع @Bean تابعًا آخر مباشرةً:
بما أن Spring يستبدل الفئة بفئة فرعية من CGLIB، فإن استدعاء passwordEncoder() داخل userService() لا يُنشئ BCryptPasswordEncoder ثانيًا. يعترض الوكيل الاستدعاء، يتحقق مما إذا كان bean بذلك الاسم موجودًا بالفعل في السياق، ويُعيد الكائن الموجود. هذا يحافظ على عقد Singleton.
@Component العادية بدلًا من @Configuration، فلن يُنشئ Spring وكيل CGLIB. تصبح استدعاءات @Bean البينية استدعاءات Java عادية وتُنشئ كائنًا جديدًا في كل مرة. هذه خطأ دقيق شائع — استخدم دائمًا @Configuration للفئات التي تستدعي توابع @Bean بعضها بعضًا.
نطاق الـ Bean
افتراضيًا كل @Bean هو Singleton — كائن واحد لكل سياق تطبيق. تُغيِّر النطاق بـ @Scope:
النطاق Prototype يعني أن Spring يُنشئ كائنًا جديدًا تمامًا في كل مرة يُطلب فيها الـ bean. النطاقات الأخرى المُدمجة (request وsession) تُستخدم في تطبيقات الويب ويدعمها سياق Servlet.
دورة الحياة: initMethod و destroyMethod
يمكنك الاشتراك في دورة حياة الـ bean دون أن تعرف فئة الـ bean شيئًا عن Spring:
يستدعي Spring التابع connect() بعد بناء الـ bean كاملًا وتعيين خصائصه، ويستدعي disconnect() عند إغلاق السياق. يُبقي ذلك الفئات الخارجية خالية من تبعيات Spring مع ربطها بدورة الحياة بشكل صحيح.
@Bean للفئات الخارجية. لفئاتك الخاصة، @PostConstruct و@PreDestroy (من jakarta.annotation) مباشرةً على فئة الـ bean أوضح — القصد مرئي هناك في الكود، وتعملان بغض النظر عن طريقة تعريف الـ bean (مسح مكوّنات أو @Bean صريح).
@Primary و @Qualifier
عندما يوجد عدة beans من نفس النوع في السياق، يحتاج Spring إلى توجيه. استخدم @Primary لتعليم الخيار الافتراضي، أو استند إلى @Qualifier عند نقطة الحقن للتحديد الصريح:
عند حقن DataSource دون تخصيص إضافي، يختار Spring الـ primaryDataSource. للحقن الصريح للنسخة: @Qualifier("reportingDataSource") عند نقطة الحقن.
تنظيم الإعداد: فئات @Configuration متعددة
التطبيق الحقيقي يحتوي على عشرات الـ beans. تقسيم الإعداد حسب الاهتمام يُبقي الملفات مركّزة وقابلة للصيانة:
يكتشف Spring Boot جميع فئات @Configuration التي يمكن الوصول إليها من حزمة فئة @SpringBootApplication عبر مسح المكوّنات. لا توجد قائمة رئيسية للإدارة — فقط ضعها في الحزمة الصحيحة.
Java Config مقابل XML مقابل مسح @Component — متى تستخدم كلًا منها
@Configurationالقائم على Java: الأفضل لـ beans البنية التحتية (مصادر البيانات وعملاء HTTP والذاكرة المؤقتة وسماسرة الرسائل وـ beans الأمان) وللكائنات الخارجية التي لا تملك كودها. منطق المصنع كود Java مرئي وقابل للاختبار.@Component/@Service/@Repository: الأفضل لفئات التطبيق الخاصة بك — الخدمات والـ repositories والمتحكمات. تشريفات أقل؛ الفئة تُعلن عن نفسها كـ bean.- XML: تراث قديم؛ تجنّبه في الكود الجديد. قد تصطدم به عند التكامل مع وحدات Spring قديمة أو مكتبات مؤسسية تأتي بفضاءات أسماء XML.
في الممارسة تمزج الأساليب الثلاثة. الإعداد التلقائي لـ Spring Boot (فئات @AutoConfiguration في كل JAR مُبدئ) مكتوب تقريبًا بالكامل كإعداد قائم على Java، ولهذا فإن فهم @Configuration و@Bean بعمق يُتيح لك قراءة أي شيء يُعدّه إطار العمل وتجاوزه.
الخلاصة
يمنحك الإعداد القائم على Java تعريفات beans آمنة النوع وقابلة للتنقل عبر بيئة التطوير في Java عادية. زيِّن الفئة بـ @Configuration ليُنشئ Spring وكيلًا لها، أعلن كل bean كتابع @Bean، وعبِّر عن التبعيات كمعاملات للتابع. استخدم @Scope لتغيير عمر الـ bean، وinitMethod/destroyMethod لربط أحداث دورة الحياة للفئات الخارجية، و@Primary/@Qualifier لحل التعارضات. قسِّم الإعدادات الكبيرة عبر فئات متعددة مركّزة — مسح المكوّنات يجدها جميعًا. في الدرس التالي نتعمق في كيفية عمل مسح المكوّنات نفسه لتفهم بالضبط أي الفئات يلتقطها Spring ومتى.