التهيئة الكسولة
التهيئة الكسولة
بصفة افتراضية، يُنشئ Spring ويُوصّل كلَّ حبّة (bean) أحادية (singleton) فور تحديث سياق التطبيق ApplicationContext — وتُسمّى هذه الاستراتيجية التهيئة المتحمّسة (eager initialization). وهذا مقصود: إذ تُكشَف أخطاء الإعداد (التبعيات المفقودة وقيم الخصائص الخاطئة) عند بدء التشغيل لا بعد دقائق حين يصل مستخدم إلى نقطة نهاية نادرة الاستخدام. وبالنسبة للغالبية العظمى من الحبوب، التهيئة المتحمّسة هي الخيار الصحيح تمامًا.
لكنّ "المتحمّسة افتراضيًا" لا تعني "المتحمّسة دائمًا". بعض الحبوب تكون مكلفة الإنشاء، أو تتصل بموارد خارجية، أو تمثّل مسارات اختيارية حقيقية لا تُستخدَم فعلًا في كثير من مثيلات التطبيق. لهذه الحالات تقدّم Spring التهيئة الكسولة (lazy initialization): لا تُنشأ الحبّة حتى يطلبها شيء ما لأول مرة. يتناول هذا الدرس متى يصحّ هذا المقايضة، وكيف تطبّقها، والمفاجآت التي يجب توقّعها.
المتحمّسة مقابل الكسولة — الفرق الجوهري
فكّر في الجدول الزمني لكلّ منهما:
- المتحمّسة: تحديث
ApplicationContext← إنشاء جميع الحبوب الأحادية وحقن التبعيات وتشغيل توابع@PostConstruct← التطبيق جاهز. - الكسولة: تحديث
ApplicationContext← تسجيل تعريف الحبّة دون إنشاء مثيل ← عند أول طلب للحبّة ← يحدث الإنشاء على خيط ذلك الطلب.
يظلّ الحاوي يتحقّق من صحة تعريف الحبّة (وجود الفئة وعدم الغموض في المُنشئ) عند بدء التشغيل. ما يُؤجَّل هو عملية new الفعلية وتوصيل التبعيات واستدعاءات دورة الحياة.
تعريف حبّة كسولة باستخدام @Lazy
ضع التعليق التوضيحي @Lazy على أي @Component أو @Service أو @Repository أو تابع @Bean ليُعامله Spring باعتباره كسولًا:
بوجود @Lazy، لن تظهر رسالة المُنشئ أثناء بدء تشغيل السياق. تظهر فقط حين تستدعي حبّة أخرى أو طلب ما تابعًا من ReportGeneratorService لأول مرة.
null — فالحقل يحتاج قيمة عند بدء التشغيل. بدلًا من ذلك يحقن وكيل CGLIB. يبدو الوكيل ويتصرّف كالحبّة الحقيقية، لكنّه يُفوّض كل استدعاء تابع إلى المثيل الفعلي، مُنشئًا إياه عند أول استدعاء. لهذا تحتاج نقاط الحقن في الحبوب المتحمّسة أيضًا إلى التعليق التوضيحي (راجع الأسفل).
@Lazy على نقطة الحقن
إذا أقدمت حبّة متحمّسة على حقن حبّة كسولة، فعليك تعليم نقطة الحقن بـ @Lazy أيضًا — وإلا أنشأ Spring الحبّة الكسولة عند بدء التشغيل لإرضاء التبعية المتحمّسة:
الوكيل الذي يُدرجه Spring شفّاف تمامًا: تستدعي توابعه بشكل طبيعي، وتعمل فحوصات المساواة، ويشارك بشكل صحيح في المعاملات وتوجيهات AOP.
التهيئة الكسولة العالمية
أضاف Spring Boot 2.2+ علامةً عالمية بسطر واحد:
يجعل هذا كلَّ حبّة كسولة بصفة افتراضية. يُعدّ شائعًا في بيئة التطوير المحلي لأنه يُقلّص وقت بدء التشغيل بشكل كبير في التطبيقات الضخمة — إذ تُنشأ فقط الحبوب التي يلمسها الطلب الأول. غير أنّ نشره في الإنتاج خطر: أخطاء الإعداد التي كانت تُوقف بدء التشغيل تُؤجَّل الآن حتى يصل أول طلب إلى الحبّة المُعطَلة، مما قد يُعيد 500 إلى أول مستخدم حقيقي.
@Lazy(false) على حبوب البنية التحتية الحرجة (أغلفة مصدر البيانات، إعداد الأمان، مؤشرات الصحة) لإجبارها على التهيئة المتحمّسة والحفاظ على التحقق عند بدء التشغيل للمسارات الهامة.
متى يكون @Lazy مناسبًا فعلًا
التهيئة الكسولة ليست حيلة أداء تُطبّقها في كل مكان. بل هي الأداة الصحيحة في حالات بعينها:
- الموارد الاختيارية المكلفة: خدمة تفتح اتصالًا بنظام قديم يستخدمه فقط وحدة تقارير المشرف. كثير من مثيلات التطبيق في مجموعة قابلة للتوسيع الأفقي قد لا تستخدمه أبدًا.
- سرعة التطوير والاختبار: الوضع الكسول العالمي يُقلّص وقت بدء التشغيل أثناء التطوير التكراري. احتفظ بالملف الشخصي للإنتاج متحمّسًا.
- حلّ التبعية الدائرية (الملاذ الأخير): كسر التبعية الدائرية بـ
@Lazyيمكن أن يُؤجّل الإنشاء بما يكفي لحلّ مشكلة الدجاجة والبيضة. غير أنّ هذا مؤشر على مشكلة في التصميم — يُفضَّل إعادة هيكلة الكود للقضاء على الدورة. - التكاملات الاختيارية: حبّة تُغلّف SDK طرف ثالث قد لا يكون مُعدًّا في جميع بيئات النشر. اقرنها بـ
@ConditionalOnPropertyللتحكم الأنظف.
متى لا تستخدم @Lazy
كذلك تجنّب الاعتماد على التهيئة الكسولة لـ "إصلاح" أوقات بدء تشغيل طويلة ناتجة عن تصميم ضعيف للحبوب. إذا كانت حبّة تستغرق ثلاث ثوانٍ في التهيئة، فتلك التكلفة لا تختفي — بل تنتقل إلى الطلب الأول، مما يُنشئ تأخيرًا حادًا عند البداية الباردة. الإصلاح الحقيقي هو جعل التهيئة نفسها أخفّ (استخدام async، تحقق أخفّ، نطاق أضيق).
الحبوب النموذجية (Prototype) والتهيئة الكسولة
الحبوب ذات النطاق النموذجي كسولة بطبيعتها — فالحاوي لا يُنشئها مسبقًا أبدًا لعدم وجود مثيل واحد يُنشئه مسبقًا. إضافة @Lazy إلى حبّة نموذجية لا يُضيف أثرًا. السلوك الذي تراه عمليًا:
التحقق من السلوك الكسول
أثناء التطوير يمكنك التحقق بسرعة من كون الحبّة كسولة فعلًا بمراقبة سجلّ بدء التشغيل. مع مستوى DEBUG للتسجيل على org.springframework، يُسجّل Spring إنشاء كل حبّة. سيظهر سطر سجلّ إنشاء الحبّة الكسولة بعد سجلّ تحديث السياق، مرتبطًا بأول استدعاء تابع لا ببدء التشغيل. فحص سريع للتحقق:
containsBean يتحقق من وجود تعريف مسجَّل؛ أما containsSingleton فيتحقق من إنشاء مثيل أحادي. الحبّة الكسولة تُظهر true / false حتى تُستخدم لأول مرة.
الخلاصة
التهيئة المتحمّسة تكشف المشكلات عند بدء التشغيل وهي الافتراض الصحيح لكل شيء تقريبًا. استخدم @Lazy بانتقائية — على الحبوب المكلفة أو الاختيارية حقًا أو التي تُحتاج فقط في مسارات كود بعينها. حين تحقن حبّة كسولة في حبّة متحمّسة، علّم نقطة الحقن بـ @Lazy أيضًا كي يُدرج Spring وكيلًا بدلًا من إجبار الإنشاء المبكر. العلامة العالمية spring.main.lazy-initialization=true مساعدة للإنتاجية في التطوير المحلي، لا تحسينًا للإنتاج. احتفظ بحبوب البنية التحتية الحرجة متحمّسةً لكي يظهر سوء الإعداد عند بدء التشغيل لا أمام المستخدمين.