تغليف التطبيق وتشغيله
تغليف التطبيق وتشغيله
كتابة الكود الصحيح ليست سوى نصف العمل. يجب أن يكون تطبيق Java الاحترافي قابلًا للبناء بشكل موثوق، وقابلًا للنقل، وقابلًا للتنفيذ على أي جهاز مستهدف — سواء كان حاسوب زميل أو خادم إنتاج. يغطّي هذا الدرس خط أنابيب التغليف الكامل: التجميع باستخدام Maven أو Gradle، وإنتاج ملفات JAR قابلة للتنفيذ، وإدارة بيئة التشغيل، وتشغيل الأداة النهائية محليًا وعلى الخادم.
فهم دورة حياة البناء
يُنمذج كلٌّ من Maven وGradle عمليات البناء كتسلسل من المراحل. فهم دورة الحياة يمنع الخطأ الشائع المتمثل في تشغيل هدف خاطئ في وقت خاطئ.
مراحل دورة حياة Maven (بالترتيب):
validate— يتحقّق من صحة المشروع وتوفّر جميع المعلومات المطلوبة.compile— يُجمّع الكود المصدري إلىtarget/classes.test— يُشغّل اختبارات الوحدة عبر Surefire؛ يفشل البناء إذا فشل أي اختبار.package— يُجمّع الكلاسات المُجمَّعة في ملف JAR (أو WAR) داخلtarget/.verify— يُشغّل فحوصات التكامل (مثل إضافة Failsafe).install— يُثبّت الأداة في مستودع Maven المحلي (~/.m2).deploy— يرفع الأداة إلى مستودع بعيد (Nexus، Artifactory، GitHub Packages).
mvn package يُشغّل تلقائيًا validate وcompile وtest أولًا. إذا أردت التجميع فقط بدون اختبارات، استخدم mvn package -DskipTests — لكن افعل ذلك فقط على جهاز المطوّر، وليس أبدًا في بيئة CI.
بناء Fat JAR قابل للتنفيذ باستخدام Maven
ملف JAR القياسي يحتوي على كلاساتك فقط. لتشغيله في أي مكان بدون classpath منفصل، تحتاج إلى fat JAR (يُسمى أيضًا uber JAR) — كلاساتك بالإضافة إلى كل اعتمادية مُجمَّعة في ملف واحد. إضافة Maven Shade هي الطريقة القياسية لإنتاجه.
بعد إضافة هذه الإضافة، شغّل:
البناء باستخدام Gradle
المكافئ في Gradle هو إضافة shadowJar (com.github.johnrengelman.shadow). أضِفها إلى build.gradle:
ثم ابنِ وشغّل:
clean قبل كل بناء للإصدار. يمكن أن تتسلّل ملفات الكلاس القديمة من تجميع سابق إلى JAR دون أن تلاحظ، مما يتسبب في أخطاء دقيقة لا تتكرر من عملية سحب جديدة. اجعل clean package (أو clean shadowJar) عادة راسخة في CI.
ملف MANIFEST.MF
نقطة دخول JAR القابل للتنفيذ مُعلَنة في META-INF/MANIFEST.MF داخل الأرشيف. السطر الحاسم هو:
إذا كان هذا السطر مفقودًا أو يشير إلى كلاس بدون طريقة public static void main(String[] args)، ستُلقي JVM خطأ java.lang.NoSuchMethodError: main أو "Main manifest attribute not found". كلتا الإضافتين المذكورتين تكتبان هذا الملف تلقائيًا عند تعريف mainClass.
إخراج الإعدادات إلى خارج التطبيق في وقت التشغيل
يجب أن يعمل JAR المبني مرة واحدة في بيئات التطوير والاختبار والإنتاج بدون إعادة تجميع. أخرج جميع القيم الخاصة بالبيئة:
مرّر خصائص النظام في وقت التشغيل:
jar tf أو unzip. استخدم متغيّرات البيئة أو مدير الأسرار (AWS Secrets Manager، HashiCorp Vault) لجميع الإعدادات الحساسة.
تحميل ملف Properties عند بدء التشغيل
للإعدادات غير الحساسة، تحميل ملف .properties عند بدء التشغيل أنظف من قائمة طويلة من أعلام -D:
أعلام JVM للضبط في الإنتاج
الإعدادات الافتراضية لـ JVM مُعايَرة لأجهزة المطوّرين. في الإنتاج، اضبط على الأقل:
التشغيل كخدمة خلفية (systemd على Linux)
على خادم Linux، غلّف JAR في وحدة systemd ليبدأ عند التشغيل ويُعيد الإقلاع عند الفشل:
اختبار الدخان للأداة المُغلَّفة
قبل النشر، تحقّق دائمًا من أن JAR يعمل من دليل نظيف — وليس من الدليل العمل لبيئة التطوير المتكاملة:
يكتشف هذا أكثر أخطاء التغليف شيوعًا: مورد أو ملف تُتيحه بيئة التطوير المتكاملة على classpath أثناء التطوير لكنه غائب من JAR المُجمَّع.
الخلاصة
الأداة الجاهزة للإنتاج تُبنى بـ mvn clean package (إضافة Shade) أو ./gradlew clean shadowJar. يُعلن MANIFEST.MF عن نقطة الدخول؛ جميع الإعدادات الخاصة بالبيئة مُخرَجة عبر خصائص النظام أو متغيّرات البيئة أو ملف properties مصاحب. تُكمل أعلام كومة JVM ووحدة systemd قصة النشر. اختبر JAR دخانيًا من دليل نظيف قبل كل إصدار للكشف عن الموارد التي تعمل فقط على classpath مبكرًا.