أدوات البناء والوحدات

أساسيات Maven

15 دقيقة الدرس 2 من 13

أساسيات Maven

Maven هي أداة البناء السائدة في نظام Java البيئي والمعيار الفعلي للمشاريع المؤسسية. إنّ الفهم العميق لها — لا مجرّد نسخ مقتطفات pom.xml — هو ما يميّز المطوّرين الذين يُسيطرون على بناءهم عن أولئك الذين يصارعونه. تغطّي هذه الدرس الركائز الثلاث التي تجعل Maven تعمل: نموذج كائن المشروع، وإحداثيات المشروع، ودورة حياة البناء.

ما الذي تفعله Maven فعليًا

Maven هي أداة بناء تعتمد مبدأ الاتفاقية على التهيئة. تُعرّف تخطيطًا قياسيًا للمشروع وتسلسلًا ثابتًا من خطوات البناء. اتّبع الاتفاقيات وستتولّى Maven تجميع الكود واختباره وتغليفه ونشره بأدنى قدر من التهيئة. حِدْ عنها وستدفع ثمنًا باهظًا من التعقيد.

تعمل Maven أيضًا كـمدير تبعيات: تنزّل مكتباتك من مستودعات مركزية، وتخزّنها محليًا، وتحلّ التبعيات المتعدية تلقائيًا. هذا موضوع الدرس 3؛ نركّز هنا على الأساسيات الهيكلية ودورة الحياة.

تخطيط المجلدات القياسي

يتّبع كل مشروع Maven هذا التخطيط تلقائيًا:

my-app/ ├── pom.xml <-- وصف البناء └── src/ ├── main/ │ ├── java/ <-- مصادر Java الإنتاجية │ └── resources/ <-- الموارد الإنتاجية في مسار الفئات └── test/ ├── java/ <-- مصادر Java للاختبارات └── resources/ <-- موارد الاختبار في مسار الفئات
احترم الاتفاقية. التخطيط ليس اعتباطيًا — تُضمَّن هذه المسارات في إضافات Maven. نقل المصادر إلى مكان آخر ممكن لكنه يستلزم تهيئة إضافية يجب أن يفهمها كل عضو جديد في الفريق. وفّر هذا التكلّف: استخدم التخطيط القياسي.

نموذج كائن المشروع (pom.xml)

ملف pom.xml هو ملف التهيئة المركزي في Maven. إنّه مستند XML يصف ما يجب بناؤه — لا كيف، فهذه مهمة Maven. تبدو صورة POM دنيا لمكتبة Java 21 كما يلي:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- إحداثيات المشروع --> <groupId>com.example</groupId> <artifactId>invoice-service</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <java.version>21</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> </project>

لكل عنصر غرضه. modelVersion دائمًا 4.0.0 للـPOM المتوافقة مع Maven 2/3/4. packaging يأخذ القيمة الافتراضية jar لكن يمكن أن يكون war أو pom (للمشاريع الأصل/المجمِّعة) أو أنواعًا مخصَّصة تضيفها الإضافات.

إحداثيات المشروع: groupId وartifactId وversion

يشكّل العنصران الثلاثة معًا معرّفًا فريدًا عالميًا للـartifact — يُكتب أحيانًا بصيغة groupId:artifactId:version (GAV). تستخدم Maven هذا الثلاثي لتخزين الـartifacts في المستودع المحلي وللإشارة إلى التبعيات.

  • groupId — يُعرّف مؤسستك أو عائلة مشروعك. استخدم اسم نطاق معكوسًا: com.example، org.apache.commons. يُرسم هذا كمسار مجلدات في المستودع.
  • artifactId — اسم هذه الوحدة تحديدًا. بأحرف صغيرة مفصولة بشرطة: invoice-service، user-api. مع groupId يُحدّد مكتبةً بعينها.
  • version — يتّبع الإصدار الدلالي (MAJOR.MINOR.PATCH). اللاحقة -SNAPSHOT لها معنى خاص: تُشير إلى بناء قيد التطوير. تعامل Maven الـSNAPSHOTs بشكل مختلف عن الإصدارات النهائية (انظر الملاحظة أدناه).
SNAPSHOT مقابل الإصدار النهائي. يمكن استبدال artifact بصيغة 1.0.0-SNAPSHOT في المستودع — تُعيد Maven تنزيله دوريًا لأن محتواه قد يتغيّر. الإصدار النهائي كـ1.0.0 ثابت بالاتفاقية: بمجرد نشره يجب ألا يتغيّر. استخدم -SNAPSHOT أثناء التطوير النشط، وأصدر نسخة نهائية حين تكون مستعدًا للشحن. لا تنشر -SNAPSHOT كتبعية إنتاجية.

الخصائص واستبدال المتغيرات

يُعرّف قسم <properties> أزواج مفتاح-قيمة تُشار إليها في أي مكان في POM بصيغة ${name}. هذا ضروري لإدارة الإصدارات بمبدأ مصدر الحقيقة الواحد:

<properties> <java.version>21</java.version> <jackson.version>2.17.1</jackson.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>${jackson.version}</version> </dependency> </dependencies>

حين تُرقّي Jackson تغيّر سطرًا واحدًا. بدون الخصائص تخاطر بأن تنجرف المكتبتان إلى إصدارات مختلفة — مصدر كلاسيكي لأخطاء وقت تشغيل خفية.

دورة حياة البناء

تُعرّف Maven ثلاث دورات حياة مدمجة. الأهم هي دورة default التي تتولّى بناء مشروعك ونشره. تعمل مراحلها بالترتيب وكل مرحلة تُشغّل المراحل السابقة تلقائيًا:

  • validate — التحقق من صحة هيكل المشروع.
  • compile — تجميع المصادر الإنتاجية.
  • test — تجميع اختبارات الوحدة وتشغيلها من src/test/java.
  • package — تغليف الكود المُجمَّع في صيغته القابلة للتوزيع (JAR، WAR…).
  • verify — تشغيل اختبارات التكامل وفحوص الجودة.
  • install — تثبيت الـartifact في المستودع المحلي (~/.m2/repository) ليصبح متاحًا للمشاريع المحلية الأخرى.
  • deploy — رفع الـartifact إلى مستودع بعيد (Nexus، Artifactory، Maven Central).

دورتا الحياة الأخريان هما clean (تحذف مجلد target/) وsite (تولّد توثيق المشروع). تشغيل mvn clean package يُنفّذ دورة clean ثم دورة default حتى مرحلة package شاملة.

# أوامر Maven الشائعة وما تفعله mvn compile # تجميع المصادر الرئيسية mvn test # التجميع + تشغيل اختبارات الوحدة mvn package # التجميع + الاختبار + إنشاء JAR/WAR في target/ mvn verify # package + اختبارات التكامل mvn install # verify + التثبيت في ~/.m2 mvn deploy # install + الرفع إلى المستودع البعيد mvn clean package # حذف target/ ثم التغليف (موصى به في CI) mvn clean install -DskipTests # بناء محلي سريع بتخطّي الاختبارات (استخدم باعتدال)
تجنّب -DskipTests في CI. تخطّي الاختبارات محليًا للتكرار السريع مقبول، لكن مسار CI الذي يتخطّى الاختبارات يهزم الغرض من CI نفسه. إن كانت الاختبارات بطيئة استثمر في تسريعها بدلًا من تخطّيها.

الإضافات وربط الأهداف

مراحل دورة حياة Maven مجردة — العمل الفعلي تؤدّيه الإضافات المرتبطة بتلك المراحل. تُعرّض كل إضافة هدفًا أو أكثر. مثلًا، تربط maven-compiler-plugin هدفها compile بمرحلة compile وهدف testCompile بمرحلة test-compile. يحدث هذا تلقائيًا لتغليف JAR.

تُهيّئ الإضافات في قسم <build>. تثبيت إصدارات الإضافات ممارسة احترافية — تضمن قابلية إعادة إنتاج البناء بعد أشهر أو سنوات:

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <release>21</release> <compilerArgs> <arg>-Xlint:all</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>

علامة <release> (مُفضَّلة على الزوج القديم <source>/<target>) تضبط إصدار Java للتجميع ومستوى البايت كود ومسار الـbootstrap classpath دفعةً واحدة، مما يمنع مشاكل إصدارات متشابكة يصعب تشخيصها.

ذاكرة التخزين المؤقت للمستودع المحلي

حين تنزّل Maven تبعيةً تخزّنها في ~/.m2/repository باستخدام مسار GAV: com/example/invoice-service/1.0.0/invoice-service-1.0.0.jar. تُعيد عمليات البناء اللاحقة استخدام الـartifact المُخزَّن دون طلب شبكي. تُشارَك هذه الذاكرة بين جميع مشاريع Maven على جهازك. على خادم CI يبدأ كل عميل بناء عادةً بذاكرة فارغة — استخدم ميزات التخزين المؤقت في نظام CI لديك للحفاظ على ~/.m2/repository بين عمليات البناء وخفض أوقاتها بشكل كبير.

الخلاصة

تُضفي Maven بنيةً من خلال الاتفاقيات: تخطيط مجلدات ثابت، وملف pom.xml تصريحي يصف مشروعك بإحداثيات (groupId:artifactId:version)، ودورة حياة default حتمية (validate → compile → test → package → verify → install → deploy). تربط الإضافات أهدافها بمراحل دورة الحياة وتؤدّي العمل الفعلي. ثبّت إصدارات الإضافات، واضبط دائمًا <release> للمُجمِّع، واستخدم الخصائص لسلاسل الإصدارات المشتركة، وأبعد الـSNAPSHOTs عن تبعيات الإنتاج. بهذه الأسس يمكنك قراءة أي مشروع Maven تُصادفه وفهمه.