إخراج الإعدادات باستخدام ملفات الخصائص
إخراج الإعدادات باستخدام ملفات الخصائص
يُعدّ ترميز قيم الإعدادات — عناوين URL لقواعد البيانات، ومفاتيح API، والمهلات الزمنية، وأعلام الميزات — مباشرةً داخل فئات Java من أكثر الأخطاء شيوعًا في مشاريع Spring المبتدئة. في اللحظة التي تحتاج فيها إلى تشغيل التطبيق ذاته مقابل قاعدة بيانات staging أو تدوير سر، ستُجبر على إعادة التصريف أو إعادة النشر. يُزيل نظام الإعدادات الخارجية في Spring هذه المشكلة بإبقاء تلك القيم في ملفات .properties خارج الكود المُصرَّف، ثم حقنها في الحبوب عند بدء التشغيل.
يتناول هذا الدرس آليتين رئيسيتين: @PropertySource لتحميل الملفات بشكل صريح، وواجهة برمجية Environment للوصول برمجيًا. يبني الدرس الخامس على هذا بتقديم @Value ولغة Spring Expression Language لحقن القيم مباشرةً في الحقول ومعاملات المُنشئ.
لماذا نُخرج الإعدادات خارج الكود؟
تُلزم منهجية تطبيق الاثني عشر عاملًا (twelve-factor app) بفصل صارم بين الكود والإعداد. عمليًا، يعني ذلك:
- تشغيل نفس ملف JAR المُصرَّف في كل بيئة — التطوير، والتكامل المستمر، والاختبار، والإنتاج.
- عدم ظهور الأسرار في نظام التحكم بالإصدارات.
- تغيير قيمة ما (كحجم مجمّع الاتصالات) دون إعادة تصريف ودون أثر بناء جديد.
- قدرة فرق التشغيل على ضبط السلوك دون الاقتراب من الكود المصدري.
التعليق @PropertySource
يُخبر @PropertySource Spring بتحميل ملف .properties وتسجيل أزواج مفتاح/قيمة الخاصة به في Environment الخاص بالتطبيق. توضعه على فئة @Configuration.
افترض وجود هذا الملف في src/main/resources/app.properties:
حمّله في فئة الإعداد:
@PropertySource هي مسار مورد Spring. يُحلّ classpath: من مسار فئات JVM (ملف JAR أو مجلد resources/). يقرأ file:/etc/myapp/secrets.properties من نظام الملفات في وقت التشغيل — مفيد للأسرار التي يُدخلها منسّق الحاويات. كذلك https:// مدعوم لكن نادرًا ما يُستخدم للخصائص.
استخدام واجهة Environment البرمجية
org.springframework.core.env.Environment هو تجريد Spring الموحَّد فوق جميع مصادر الخصائص. بمجرد تحميل ملف عبر @PropertySource، تصبح مفاتيحه متاحة من خلال هذه الواجهة في أي مكان في سياق التطبيق.
الأساليب الرئيسية التي تحتاج معرفتها:
env.getProperty("key")— تُعيد القيمة أوnullإن لم يكن المفتاح موجودًا.env.getProperty("key", "defaultValue")— تُعيد القيمة أو قيمة افتراضية.env.getProperty("key", Integer.class)— تُعيد القيمة مُحوَّلة إلى النوع المطلوب، أوnull.env.getProperty("key", Integer.class, 10)— بحث بنوع مع قيمة افتراضية.env.getRequiredProperty("key")— يرميIllegalStateExceptionإن كان المفتاح غائبًا؛ هو الخيار المُفضَّل للقيم الإلزامية.env.containsProperty("key")— فحص منطقي، مفيد لأعلام الميزات الاختيارية.
ملفات متعددة والتراكم
يمكنك تحميل عدة ملفات على فئة واحدة باستخدام التعليق القابل للتكرار في Java 8:
عندما يوجد المفتاح ذاته في مصادر متعددة، يفوز المصدر الأخير المُسجَّل. هذا يجعل من السهل شحن قيم افتراضية معقولة والسماح لملف خاص بالبيئة بتجاوز ما يختلف فحسب.
application.properties) في نظام التحكم بالإصدارات مع الإعدادات الافتراضية غير الحساسة وتعليقات الاستبدال. ملف مرافق كـ application-local.properties (مُدرَج في git-ignore) يحمل التجاوزات الخاصة بالمطوّر. في Spring Boot هذا مدمج؛ في Spring العادي تصله يدويًا عبر @PropertySource.
تجاهل الملفات الغائبة باستخدام ignoreResourceNotFound
بشكل افتراضي، إن لم يكن الملف المُحدَّد في @PropertySource موجودًا، يرمي Spring استثناءً عند بدء تشغيل السياق. يمكنك تخفيف هذا للملفات الاختيارية:
استخدم هذا للملفات الاختيارية — تجاوزات المطوّر، والأسرار المحلية، وتركيبات الاختبار — حتى يبدأ التطبيق بنظافة حتى في غياب الملف.
العناصر النائبة للخصائص داخل القيم
يُحلّ Spring بنية ${placeholder} داخل قيم الخصائص، مما يسمح لك بتأليف قيم من خصائص أخرى:
يعمل هذا تلقائيًا بمجرد تسجيل حبة PropertySourcesPlaceholderConfigurer (في Spring العادي) أو الاعتماد على الإعداد التلقائي لـ Spring Boot الذي يُسجّله من أجلك.
${...} في تعليقات @Value وإعدادات XML إلا بوجود حبة PropertySourcesPlaceholderConfigurer. صرّح عنها كحبة static حتى تُعالَج قبل الحبوب الأخرى:
@PropertySource مع مصنع مخصص: الترميز والتنسيقات المخصصة
بشكل افتراضي، يقرأ Spring ملفات .properties بترميز ISO-8859-1. إن احتوت قيمك على محارف UTF-8 (نصوص عربية، حروف مُنقَّطة، محارف CJK) فيجب أن تُصرّح بالترميز صراحةً:
للتنسيقات غير المعيارية (YAML، TOML، ملفات الخصائص المشفّرة)، يتيح Spring مصنع PropertySourceFactory مخصصًا:
ثم أشر إليه في التعليق:
application.yml تلقائيًا بواسطة YamlPropertySourceLoader — لا حاجة لمصنع مخصص. نمط المصنع المخصص مفيد أساسًا في Spring العادي أو عند تحميل ملفات إضافية بتنسيقات غير معيارية إلى جانب الإعداد الرئيسي.
أولوية مصادر الخصائص
يحتفظ Spring بـقائمة ذات أولويات من مصادر الخصائص. في Spring Boot يكون الترتيب (الأعلى أولوية أولًا) تقريبًا:
- معطيات سطر الأوامر (
--server.port=9090) - متغيرات بيئة نظام التشغيل (
SPRING_DATASOURCE_URL) - خصائص نظام JVM (
-Dspring.profiles.active=prod) - خصائص خاصة بالمرحلة (
application-prod.properties) - الخصائص الافتراضية (
application.properties) - الملفات المضافة عبر
@PropertySource
تقع الملفات التي تحمّلها بـ @PropertySource في قاع هذه الرزمة. هذا مقصود: يعني ذلك أن متغيرات بيئة النظام وسطر الأوامر تتجاوز دائمًا ما في ملفات الخصائص المُدرجة في الكود، مما يمنح فرق التشغيل السيطرة الكاملة في وقت التشغيل دون أن تمسّ الكود المصدري.
الخلاصة
@PropertySource هو الطريقة الصريحة المدفوعة بالتعليقات لتحميل ملف .properties في Environment الخاص بـ Spring. بمجرد تحميله، تصبح المفاتيح متاحة عبر Environment.getProperty() أو getRequiredProperty(). يمكنك تراكم ملفات متعددة، وتجاهل الملفات الاختيارية الغائبة، وتأليف القيم بعناصر ${} النائبة، بل وإضافة مصانع مخصصة للتنسيقات غير المعيارية. في الدرس التالي ستتعلم كيف تتخطى استدعاءات Environment API كليًا وتحقن القيم مباشرةً في حقول الحبة ومعاملات المُنشئ باستخدام @Value.