إدارة التبعيات

6 دقيقة

جدول المحتويات


1 — ما التبعية؟

التبعية مكتبة خارجية يحتاجها مشروعك للتجميع أو التشغيل. بدل إعادة كتابة الشفرة (تحليل JSON، الوصول إلى قاعدة بيانات، اختبارات…)، نعيد استخدام مكتبات مجرَّبة.

يدير Maven هذه التبعيات تلقائياً: تصرّح بها في pom.xml، فينزّلها Maven ويضعها على الـ classpath.

بلا Mavenمع Maven
تنزيل كل .jar يدوياًالتصريح بثلاثة أسطر في pom.xml
إدارة الـ classpath يدوياًيبنيه Maven تلقائياً
إيجاد مكتبات متوافقةحل انتقالي تلقائي

تُعرَّف التبعية أيضاً بإحداثيات GAV (groupId:artifactId:version)، تماماً كمشروعك. هكذا يعرف Maven ماذا ينزّل.

↑ العودة إلى الأعلى


2 — التصريح بتبعية

تُصرَّح التبعيات في الكتلة <dependencies> من pom.xml. كل تبعية وسم <dependency> بإحداثيات GAV.

xml
<dependencies>

    <!-- مكتبة Gson من Google لمعالجة JSON -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>

</dependencies>

لإيجاد الإحداثيات الصحيحة لمكتبة، نراجع Maven Central (search.maven.org)، الذي يوفّر كتلة XML جاهزة للنسخ.

bash
# بعد إضافة تبعية، نطلق التنزيل
mvn compile

# أو التنزيل صراحة دون تجميع
mvn dependency:resolve
العنصرالدور
<groupId>المنظمة التي تنشر المكتبة
<artifactId>اسم المكتبة
<version>الإصدار المطلوب

يضع Maven المكتبات في تخزين ~/.m2/repository. مكتبة نُزّلت لا تُنزَّل ثانية: لذلك البناءات التالية سريعة.

تمرين مصغّر — صرّح بتبعية Gson (com.google.code.gson:gson:2.10.1) في كتلة <dependency>.

عرض الحل
xml
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>

↑ العودة إلى الأعلى


3 — النطاقات (scopes)

النطاق (scope) لتبعية يبيّن متى وأين تكون متاحة: عند التجميع، أو الاختبارات، أو التشغيل، أو يوفّرها البيئة. نحدّده بـ <scope>.

xml
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>   <!-- متاحة للاختبارات فقط -->
</dependency>
النطاقمتاحة عند التجميعمتاحة عند الاختباراتمتاحة عند التشغيلمضمَّنة في الأثر
compile (افتراضي)نعمنعمنعمنعم
testلانعملالا
providedنعمنعملا (يوفرها الخادم)لا
runtimeلانعمنعمنعم

أمثلة ملموسة :

المكتبةالنطاق النموذجيلماذا
JUnittestمفيدة فقط لتشغيل الاختبارات
API Servletprovidedالخادم (Tomcat) يوفّرها أصلاً
مشغّل JDBCruntimeمطلوب عند التشغيل، لا عند التجميع
Gsoncompileمستخدمة في كل الشفرة

وضع JUnit في compile بدل test خطأ شائع: يضمّن مكتبة الاختبار بلا داعٍ في المخرج النهائي. اختر دائماً النطاق الأضيق المناسب.

تمرين مصغّر — صرّح بتبعية JUnit Jupiter (5.10.2) بنطاق اختبار في pom.xml.

عرض الحل
xml
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>
</dependency>

↑ العودة إلى الأعلى


4 — المستودعات (repositories)

ينزّل Maven التبعيات من مستودعات (repositories). هناك ثلاثة مستويات :

نوع المستودعالوصف
المستودع المحليتخزين على جهازك: ~/.m2/repository
Maven Centralالمستودع العام العالمي، المصدر الافتراضي
مستودع خاصخادم شركة (Nexus، Artifactory) للمكتبات الداخلية

لإضافة مستودع خاص، نصرّح به في pom.xml :

xml
<repositories>
    <repository>
        <id>nexus-entreprise</id>
        <url>https://nexus.monentreprise.com/repository/maven-public/</url>
    </repository>
</repositories>
bash
# فرض تحديث التبعيات من المستودعات
mvn clean install -U

# إفراغ تخزين مكتبة لفرض إعادة تنزيلها
# (حذف يدوي للمجلد الموافق في ~/.m2)
الحالةالمستودع المستخدم
مكتبة عامة مفتوحة المصدرMaven Central
مكتبة داخلية للشركةمستودع خاص (Nexus/Artifactory)
مكتبة نُزّلت مسبقاًالتخزين المحلي ~/.m2

تستخدم الشركات غالباً مستودعاً خاصاً كـ مرآة لـ Maven Central: يسرّع التنزيلات ويسمح بالتحكم في المكتبات المسموحة.

↑ العودة إلى الأعلى


5 — الحل الانتقالي

قد تعتمد تبعية نفسها على مكتبات أخرى: هذه التبعيات الانتقالية. ينزّلها Maven تلقائياً — لا تحتاج إلى سردها.

تصرّح بـ تبعية واحدة، ويحل Maven عشرات :

xml
<!-- تصريح واحد... -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.2.5</version>
</dependency>
<!-- ...يجلب تلقائياً spring-web و jackson و tomcat وغيرها -->

لعرض الشجرة كاملة :

bash
# يعرض شجرة التبعيات (مباشرة + انتقالية)
mvn dependency:tree

خرج نموذجي :

com.exemple:mon-app:jar:1.0.0
+- org.springframework.boot:spring-boot-starter-web:jar:3.2.5:compile
|  +- org.springframework:spring-web:jar:6.1.6:compile
|  +- com.fasterxml.jackson.core:jackson-databind:jar:2.15.4:compile
|  \- org.apache.tomcat.embed:tomcat-embed-core:jar:10.1.20:compile

عندما يجلب مساران إصدارين مختلفين للمكتبة نفسها، يطبّق Maven قاعدة «الأقرب في الشجرة يفوز» (nearest wins): الإصدار الأقرب إلى مشروعك هو الذي يُعتمد.

الحل الانتقالي من أقوى ميزات Maven: تصرّح بنواياك على مستوى عالٍ، ويجمع Maven أحجية التبعيات نيابة عنك.

تمرين مصغّر — اكتب الأمر الذي يعرض شجرة التبعيات الكاملة (مباشرة وانتقالية).

عرض الحل
bash
mvn dependency:tree

↑ العودة إلى الأعلى


6 — التشخيص والاستبعاد

أحياناً تطرح تبعية انتقالية مشكلة (تعارض إصدار، مكتبة غير مرغوبة). يوفّر Maven أدوات للتشخيص والتصحيح.

لـ استبعاد تبعية انتقالية غير مرغوبة :

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.2.5</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
        </exclusion>
    </exclusions>
</dependency>

أوامر تشخيص مفيدة :

الأمرالدور
mvn dependency:treeيعرض شجرة التبعيات الكاملة
mvn dependency:analyzeيكتشف التبعيات غير المستخدمة أو الناقصة
mvn dependency:resolveينزّل كل التبعيات
bash
# تحديد التبعيات المصرَّح بها وغير المستخدمة،
# وتلك المستخدمة وغير المصرَّح بها
mvn dependency:analyze

قبل استبعاد أي شيء، شغّل mvn dependency:tree لفهم من أين تأتي التبعية المشكلة. لا نستبعد أبداً على عمى.

تمرين مصغّر — اكتب الأمر الذي يحدد التبعيات المصرَّح بها وغير المستخدمة (والعكس).

عرض الحل
bash
mvn dependency:analyze

↑ العودة إلى الأعلى


7 — اختبار — إدارة التبعيات

السؤال 1: أين يخزّن Maven التبعيات المنزَّلة؟

a) في target/

b) في ~/.m2/repository

c) في src/main/resources

d) على خادم الويب

عرض الحل

الإجابة: b) — المستودع المحلي ~/.m2/repository يعمل تخزيناً: مكتبة نُزّلت لا تُنزَّل ثانية.


السؤال 2: أي نطاق يناسب JUnit، مكتبة تُستخدم للاختبارات فقط؟

a) compile

b) runtime

c) test

d) provided

عرض الحل

الإجابة: c) — يجعل النطاق test التبعية متاحة للاختبارات فقط ويستبعدها من المخرج النهائي.


السؤال 3: ما التبعيات الانتقالية؟

a) تبعيات يجب سردها يدوياً

b) تبعيات تبعياتك، يحلها Maven تلقائياً

c) تبعيات مؤقتة

d) تبعيات اختبار

عرض الحل

الإجابة: b) — تبعية مصرَّح بها تجلب تبعياتها؛ ينزّلها Maven تلقائياً.


السؤال 4: أي أمر يعرض شجرة التبعيات الكاملة؟

a) mvn list

b) mvn dependency:tree

c) mvn show-deps

d) mvn package

عرض الحل

الإجابة: b) — يعرض mvn dependency:tree التبعيات المباشرة والانتقالية، مفيداً لتشخيص التعارضات.


السؤال 5: أي نطاق تختار لـ API Servlet التي يوفّرها خادم Tomcat؟

a) compile

b) test

c) provided

d) runtime

عرض الحل

الإجابة: c)provided: المكتبة لازمة للتجميع، لكن بيئة التشغيل (الخادم) توفّرها أصلاً.

↑ العودة إلى الأعلى


8 — تطبيق عملي — إضافة تبعيات

المطلوب

في مشروع قائم، أضف تبعيتين: Gson (com.google.code.gson:gson:2.10.1) بنطاق compile لمعالجة JSON، و JUnit Jupiter (org.junit.jupiter:junit-jupiter:5.10.2) بنطاق test. ثم تحقق من شجرة التبعيات.


التصحيح — كتلة <dependencies> المتوقعة

xml
<dependencies>

    <!-- Gson: مستخدمة في كل الشفرة (نطاق compile افتراضي) -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>

    <!-- JUnit: الاختبارات فقط -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.2</version>
        <scope>test</scope>
    </dependency>

</dependencies>

التحقق :

bash
# تنزيل وعرض شجرة التبعيات
mvn dependency:tree

النتيجة المتوقعة :

com.exemple:mon-app:jar:1.0.0-SNAPSHOT
+- com.google.code.gson:gson:jar:2.10.1:compile
\- org.junit.jupiter:junit-jupiter:jar:5.10.2:test
   +- org.junit.jupiter:junit-jupiter-api:jar:5.10.2:test
   +- org.junit.jupiter:junit-jupiter-params:jar:5.10.2:test
   \- org.junit.jupiter:junit-jupiter-engine:jar:5.10.2:test

لاحظ أن junit-jupiter يجلب تلقائياً -api و -params و -engine: هذه تبعياته الانتقالية. صرّحت بسطر واحد، وحل Maven الباقي.

↑ العودة إلى الأعلى


9 — الخلاصة

النقاط التي يجب تذكّرها

  1. التبعية مكتبة خارجية، معرَّفة بإحداثيات GAV.
  2. نصرّح بها في <dependencies>؛ ينزّلها Maven ويخزّنها في ~/.m2.
  3. النطاقات (compile، test، provided، runtime) تتحكم في متى تكون التبعية متاحة.
  4. المستودعات: محلي (~/.m2)، Maven Central (عام)، خاص (Nexus/Artifactory).
  5. الحل الانتقالي يجلب تلقائياً تبعيات التبعيات؛ يسمح mvn dependency:tree بفحصها.

ما يلي

الدرس 04 — دورة الحياة والمراحل: فهم المراحل (compile، test، package، install، deploy) التي تنسّق البناء.

↑ العودة إلى الأعلى


جميع الحقوق محفوظة. يُحظَر نسخ هذه الدورة أو نشرها أو استخدامها أو تكييفها، كلياً أو جزئياً، دون إذن كتابي مسبق من الدكتور هيثم رحومة.

دورة من إعداد الدكتور هيثم رحومة — تطوير ونشر حلول البيانات