مقدمة: لعنة آلاف JARs
\nهل سبق لك أن وجدت نفسك في موقف حيث ينمو مشروع Java الخاص بك، ويتحول مجلد lib/ إلى حديقة حيوانات حقيقية من عشرات، بل مئات ملفات JAR؟ وفي كل مرة تقوم فيها بتشغيل التطبيق، تتذكر بشكل مؤلم أي JAR قمت بإضافته وأيها نسيت؟ مألوف، أليس كذلك؟
سؤال "Including all the jars in a directory within the Java classpath" هو أحد أكثر الأسئلة شيوعًا على Stack Overflow. وليس من دون سبب. فالمهمة التي تبدو بسيطة تتحول إلى صداع إذا كنت لا تعرف الأدوات الصحيحة. في هذه المقالة، سنستعرض جميع الطرق: من القديمة مثل الديناصورات، إلى الأكثر حداثة التي يستخدمها كل مطور Java يحترم نفسه في عام 2025.
\nاستعدوا، نحن ننطلق في رحلة مثيرة عبر غابات classpath!
\n\nلماذا هذه مشكلة أصلًا؟ (أو "ملحمة -cp")
\nفي الأيام الخوالي (قبل Java 6)، كان المطورون مضطرين لسرد كل ملف JAR يدويًا. كان يبدو الأمر هكذا تقريبًا:
\njava -cp lib/guava.jar:lib/spring-core.jar:lib/log4j.jar:lib/jackson.jar MyApp\nوالآن تخيل أن لديك 30 مكتبة. خطأ مطبعي واحد - ويسقط التطبيق مع ClassNotFoundException. كابوس! عندها بدأ المجتمع في البحث عن طرق لتضمين جميع jar في classpath Java بسطر واحد.
الطريقة رقم 1: Wildcard — المنقذ من Java 6
\nابتداءً من Java 6، ظهرت إمكانية سحرية في JVM — wildcard (رمز الاستبدال). ما عليك سوى تحديد المسار إلى المجلد مع علامة النجمة، وJVM نفسها تقوم بتحميل جميع ملفات JAR من هناك.
\nكيف يعمل هذا:
\njava -cp "lib/*" MyApp\nمهم! لاحظ علامات الاقتباس. بدونها، في بعض الأصداف (مثل Unix)، قد يتم تفسير علامة النجمة بواسطة shell، وستحصل على شيء غير متوقع. تضمن علامات الاقتباس أن * يتم تمريره إلى JVM.
لا تزال هذه الطريقة صالحة حتى اليوم. إنها بسيطة وأنيقة وتحل 90% من المشاكل. لكن هناك فارق بسيط: wildcard لا يعمل بشكل متكرر. إذا كان لديك مجلدات متداخلة (lib/subdir/)، فلن يتم تحميل محتوياتها. لهذا تحتاج إلى تحديد كل مستوى يدويًا:
java -cp "lib/*:lib/subdir/*" MyApp\n\nمتى تستخدم wildcard؟
\n- \n
- أنت تكتب سكريبتًا بسيطًا أو أداة مساعدة. \n
- ليس لديك نظام بناء (Maven/Gradle). \n
- تحتاج إلى تشغيل شيء بسرعة "على الركبة". \n
الطريقة رقم 2: Maven — معيار الصناعة
\nإذا كنت تعمل على مشروع حقيقي، فمن المحتمل أنك تستخدم Maven. وسؤال "كيفية تضمين جميع JARs من مجلد" يتم حله هنا على مستوى البناء.
\nMaven يدير التبعيات بنفسه. ما عليك سوى تحديدها في pom.xml، ويقوم Maven بتنزيل ملفات JAR المطلوبة وإضافتها إلى classpath تلقائيًا.
مثال على pom.xml:
<dependencies>\n <dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-web</artifactId>\n <version>3.2.0</version>\n </dependency>\n <dependency>\n <groupId>com.google.code.gson</groupId>\n <artifactId>gson</artifactId>\n <version>2.10.1</version>\n </dependency>\n</dependencies>\nأثناء البناء (مثل mvn package)، يقوم Maven بإنشاء fat JAR (أو يستخدم إضافة maven-dependency-plugin)، والذي يتضمن جميع التبعيات. لم تعد بحاجة للتفكير في classpath - فهو معبأ بالداخل.
ولكن ماذا لو كنت بحاجة إلى تضمين جميع JARs من مجلد محلي لا يديره Maven؟ لهذا هناك System Dependencies (على الرغم من أن هذا يعتبر نمطًا معاكسًا):
\n<dependency>\n <groupId>com.example&