«تطبيقك يعمل فقط في IDE؟ حان الوقت لإصلاح ذلك!»
\n\nإذا سبق لك أن كتبت بلغة Java، فمن المحتمل أنك واجهت هذا الموقف: تظهر بفخر مشروعك الجديد لصديق، وتشغله في IntelliJ IDEA أو Eclipse - كل شيء يعمل بشكل مثالي. ولكن بمجرد أن تسلم هذا المشروع لزميل أو تحاول تشغيله عبر سطر الأوامر، تستقبلك رسالة ClassNotFoundException أو NoClassDefFoundError. هل هذا مألوف؟
\n\nفي معظم الدورات التعليمية والبرامج التعليمية، يوضحون كيفية إنشاء ملف JAR باستخدام Maven، لكنهم غالبًا ما يغفلون تفصيلًا مهمًا: أين تذهب التبعيات؟. ملف JAR العادي يحتوي فقط على الكود الخاص بك، بينما تبقى جميع المكتبات (مثل Apache Commons، Jackson، JUnit) خارجة. ولهذا السبب يظهر السؤال الذي يطرحه ملايين مطوري Java على Stack Overflow: «How can I create an executable/runnable JAR with dependencies using Maven?»
\n\nفي هذه المقالة، سنستعرض عدة طرق مجربة لتجميع JAR قابل للتنفيذ مع التبعيات باستخدام Maven. ستتعلم كيفية إعداد maven-assembly-plugin و maven-shade-plugin وحتى كيفية عمل ذلك في Spring Boot. هيا بنا!
\n\nلماذا لا يعمل JAR العادي؟
\n\nلنقم بتجربة بسيطة. تخيل أن لديك تطبيقًا يقوم بتحليل JSON باستخدام مكتبة Jackson. تقوم بإنشاء JAR عادي بأمر mvn package، ثم تحاول تشغيله:
java -jar my-app.jar\n\nوستحصل على شيء مثل:
\n\nException in thread "main" java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ObjectMapper\n\nلماذا يحدث هذا؟ لأن Maven بشكل افتراضي يقوم بتغليف الكود المترجم الخاص بك فقط (الفئات من مجلد src/main/java). جميع التبعيات التي حددتها في pom.xml تبقى في المستودع المحلي لـ Maven (~/.m2/repository). عند تشغيل JAR، لا تعرف JVM أين تبحث عن هذه المكتبات، فتصدر خطأ.
الحل هو إنشاء fat JAR (JAR سميك) أو uber JAR، والذي يحتوي بداخله ليس فقط على فئاتك ولكن أيضًا على جميع التبعيات الضرورية. ولهذا الغرض توجد إضافات Maven خاصة.
\n\nالطريقة 1: maven-assembly-plugin (بسيط وموثوق)
\n\nالطريقة الأكثر شيوعًا وبساطة هي استخدام maven-assembly-plugin. يقوم بإنشاء JAR حيث يتم فك ضغط جميع التبعيات ودمجها مع فئاتك. دعنا نضبطه.
\n\nأضف الكود التالي إلى pom.xml الخاص بك في قسم <build><plugins>:
<plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-assembly-plugin</artifactId>\n <version>3.6.0</version>\n <configuration>\n <descriptorRefs>\n <descriptorRef>jar-with-dependencies</descriptorRef>\n </descriptorRefs>\n <archive>\n <manifest>\n <mainClass>com.example.Main</mainClass>\n </manifest>\n </archive>\n </configuration>\n <executions>\n <execution>\n <id>make-assembly</id>\n <phase>package</phase>\n <goals>\n <goal>single</goal>\n </goals>\n </execution>\n </executions>\n</plugin>\n\nهام: استبدل com.example.Main بالاسم الكامل لفئتك التي تحتوي على طريقة main.
الآن قم ببناء المشروع بالأمر:
\n\nmvn clean package\n\nفي مجلد target سيظهر ملفان:
- \n
my-app.jar- عادي