بنية المشروع وpom.xml
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
In the last lesson you saw that a build tool follows a "card" kept inside the project. In Maven that card is a file named pom.xml. This lesson teaches the two things you need to read any Maven project: the folder layout Maven expects, and the parts of the pom.xml.
Maven works by convention over configuration: if you put files where Maven expects them, you hardly need to tell it anything. That is why the wrong folder layout is the most common beginner error, and why we start there.
Why this matters: every Java backend project you open at work will have this shape. Reading it fluently saves hours. This lesson builds on lesson 1 (why build tools) and on the packages and folders you used in earlier Java modules.
Learning Objectives
By the end of this lesson you will be able to:
- Place main code, test code, and
pom.xmlin the correct Maven locations. - Say what the
target/folder is and why it is not part of your source. - Name and explain the three coordinates:
groupId,artifactId,version. - Read a
pom.xmland find its properties, dependencies, and plugins. - Explain what
<modelVersion>and<packaging>mean. - Recognize the error Maven prints when a required coordinate is missing.
Written Explanation
The standard directory layout
Maven expects this shape (this is the layout used throughout the module):
hello/
├── pom.xml
└── src/
├── main/
│ └── java/
│ └── com/example/Greeter.java
└── test/
└── java/
└── com/example/GreeterTest.javaThe rules:
| Path | What goes there |
|---|---|
pom.xml | The project description, at the root of the project |
src/main/java | Your application code (package folders start here) |
src/test/java | Your test code (same packages as the code under test) |
src/main/resources | Non-code files the program needs, such as configuration files (optional) |
target/ | Everything Maven generates: compiled classes, reports, the JAR |
Notice that the package com.example becomes the folders com/example under src/main/java. The folder src/main/java itself is not part of the package name. You already did this in earlier modules; Maven just fixes the starting folder.
Test code lives in a separate tree so it is compiled and run for the tests but is not packaged into the final JAR. The test class GreeterTest is in the same package as Greeter, which lets it use package-private members.
The target/ folder
After a build, a target/ folder appears. It holds compiled .class files (target/classes), compiled tests (target/test-classes), test reports (target/surefire-reports), and the JAR. Real listing from a build of the example project:
target/classes
target/test-classes
target/surefire-reports
target/hello-1.0-SNAPSHOT.jarEverything in target/ can be rebuilt from your sources at any time, so it is never edited by hand and never committed to Git. Deleting it is always safe.
The pom.xml
POM stands for Project Object Model. The pom.xml is an XML file that describes your project and tells Maven how to build it. XML is made of nested tags such as <name>value</name>; you only need to read it, not master it.
Here is the complete pom.xml of the example project:
<?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>hello</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.12.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>Read it top to bottom:
- The opening
<project ...>lines are boilerplate: they name the XML format. Copy them; do not worry about them. <modelVersion>is the version of the POM format itself. It is4.0.0for all current Maven projects.- Coordinates (next section) say who and what this project is.
<properties>are named settings.maven.compiler.releasesets the Java version used to compile;project.build.sourceEncodingsets the text encoding of source files. Always set both, as explained below.<dependencies>lists the libraries the project uses (lesson 3).
If <packaging> is not written, Maven assumes jar, which is what we want.
Coordinates
Every Maven project is identified by three values:
| Coordinate | Meaning | Example |
|---|---|---|
groupId | The organization or group that owns it, usually a reversed domain name | com.example |
artifactId | The name of this project (and of its JAR) | hello |
version | Which version of the project this is | 1.0-SNAPSHOT |
They are written together as groupId:artifactId:version, here com.example:hello:1.0-SNAPSHOT. The same three values identify every library you depend on, which is how Maven finds them in a repository.
The suffix -SNAPSHOT marks a version still under development. The JAR name follows the pattern artifactId-version.jar, which is why the real file is hello-1.0-SNAPSHOT.jar.
If a coordinate is missing, Maven refuses to start. Removing groupId from a test project gives this real message:
[ERROR] The project [unknown-group-id]:demo:1.0 (...pom.xml) has 1 error
[ERROR] 'groupId' is missing. @ line 1, column 52Why set the Java version
Without a Java version property, Maven uses defaults that may be much older than your JDK. In a test project with no properties, the real build output was:
[INFO] Compiling 1 source file with javac [debug target 1.8] to target\classes
[WARNING] source value 8 is obsolete and will be removed in a future releaseThat is Java 8 compatibility settings on a JDK 21: modern syntax would fail, and the warnings are noise. The default depends on the Maven compiler plugin version, so do not rely on it: set maven.compiler.release explicitly. (release tells javac which Java version's language and library to compile against.) With the property set to 21, the build compiled with no such warning.
Plugins
Maven itself knows very little. The work (compiling, running tests, creating the JAR) is done by plugins, small programs Maven downloads on demand. You can see them in the build output:
[INFO] --- compiler:3.13.0:compile (default-compile) @ hello ---
[INFO] --- surefire:3.2.5:test (default-test) @ hello ---
[INFO] --- jar:3.4.1:jar (default-jar) @ hello ---Each line reads plugin:version:goal. The compiler plugin calls javac, Surefire runs tests, the jar plugin builds the JAR. Notice that our pom.xml mentions no plugins at all: the default ones are built into Maven's conventions. You declare a plugin in a <build><plugins> section only when you want to add something or change a default. Here is what such a section looks like (you will not need one in this module):
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.1</version>
</plugin>
</plugins>
</build>Plugins have coordinates too: the same groupId:artifactId:version idea.
Where the analogy stops
The recipe card lists steps. A pom.xml mostly lists facts (name, Java version, libraries); the steps come from Maven's built-in conventions. That is why a tiny pom.xml can drive a full build.
Examples
Example 1: sorting files into the layout (simple)
Where do these belong?
| File | Location |
|---|---|
pom.xml | project root |
Greeter.java (package com.example) | src/main/java/com/example/Greeter.java |
GreeterTest.java | src/test/java/com/example/GreeterTest.java |
application.properties (config file) | src/main/resources/ |
hello-1.0-SNAPSHOT.jar | target/ (generated, do not place it yourself) |
Example 2: labeling a pom (practical)
<groupId>com.shop</groupId>
<artifactId>orders</artifactId>
<version>2.3.0</version>
...
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.21.0</version>
</dependency>The first three lines are this project's coordinates: com.shop:orders:2.3.0. The <dependency> block is a library, with its own coordinates org.apache.commons:commons-lang3:3.21.0. Same three fields, different roles: the project's own coordinates sit directly under <project>; dependency coordinates sit inside <dependency>.
Example 3: a minimal valid project (realistic)
The smallest pom.xml Maven accepts has just modelVersion and the three coordinates. This one passed mvn validate and then built:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>1.0</version>
</project>[INFO] Building demo 1.0
[INFO] BUILD SUCCESSReal projects add properties, dependencies, and sometimes plugins on top of this core. The minimal-element rule is described in Maven's POM introduction (see References).
Backend Insight
A backend repository is usually opened by many people over years. Because Maven projects share one layout, a new developer finds the code in src/main/java, the tests in src/test/java, and the dependencies in pom.xml without being told. The groupId also becomes the package prefix people recognize: when you see com.shop.orders in a stack trace, you know whose code it is. Later, when the project is packaged, the artifactId and version in the file name tell operations exactly which build is deployed.
Common Mistakes
Mistake 1: Wrong folder layout
- Incorrect: tests in
src/main/java, or sources in a folder calledsrcwith nomain/java. - Correct: code in
src/main/java, tests insrc/test/java. Maven looks only there by default, so misplaced files are ignored or fail to compile (lesson 6 shows the real error for tests in the wrong tree).
Mistake 2: Putting the package folder name into src/main/java twice
If Greeter.java declares package com.example; it belongs in src/main/java/com/example/, not in src/main/java/main/java/com/example/.
Mistake 3: Not setting the Java version
Leaving out maven.compiler.release can compile for an old Java level and print obsolete warnings. Set it to the version of your JDK (21 in this course).
Mistake 4: Editing or committing target/
target/ is output. Edits there vanish at the next build, and committing it fills the repository with generated files (lesson 6 handles .gitignore).
Mistake 5: Mixing up the project's coordinates and a dependency's
A groupId directly under <project> names your project. A groupId inside <dependency> names a library. Changing the wrong one makes Maven look for your own project on the internet or rename your JAR.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. Write the folder where Order.java (package com.shop.model) belongs, and the folder where OrderTest.java (same package) belongs.
أظهر الحل A1
A1. src/main/java/com/shop/model/Order.java and src/test/java/com/shop/model/OrderTest.java.
A2. What are the three coordinates? What does -SNAPSHOT mean?
أظهر الحل A2
A2. groupId (owner), artifactId (project name), version. -SNAPSHOT marks a version that is still in development.
A3. Which folder holds everything Maven generates? Should it be committed to Git?
أظهر الحل A3
A3. target/. No: it is generated and can always be rebuilt.
Level B: Application
B1. In the following fragment, identify the project's coordinates and the dependency's coordinates, and write the JAR file name Maven will build (the default packaging applies).
<groupId>com.bank</groupId>
<artifactId>ledger</artifactId>
<version>0.4.1</version>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.21.0</version>
</dependency>
</dependencies>أظهر الحل B1
B1. Project: com.bank:ledger:0.4.1. Dependency: org.apache.commons:commons-lang3:3.21.0. JAR: ledger-0.4.1.jar (artifactId, a dash, and the version).
B2. Your colleague's project compiles with warnings that "source value 8 is obsolete" although everyone runs JDK 21. Which part of the pom.xml is probably missing, and what would you add?
أظهر الحل B2
B2. The Java version property. Add <properties><maven.compiler.release>21</maven.compiler.release></properties> (and the source encoding property).
B3. A pom.xml with no <groupId> fails. What is the message about, and what is the fix?
أظهر الحل B3
B3. It says 'groupId' is missing (the project's own groupId, because it is a required coordinate). Add a <groupId>, for example com.example, next to artifactId and version.
Level C: Challenge
C1. Draw the full folder tree for a project inventory (com.shop:inventory:1.0) with two main classes Item and Stock, one test class StockTest, one configuration file app.properties, and the generated JAR.
أظهر الحل C1
C1.
inventory/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/com/shop/Item.java
│ │ ├── java/com/shop/Stock.java
│ │ └── resources/app.properties
│ └── test/java/com/shop/StockTest.java
└── target/inventory-1.0.jar (generated)(The package com.shop is assumed from the groupId; any consistent package is acceptable.)
C2. A teammate says: "Our pom lists no plugins, so Maven can't compile or run tests." Explain why this is wrong.
أظهر الحل C2
C2. Maven's default build uses default plugins that are bound to the phases by convention; they appear in the build output even though the pom.xml does not declare them. You declare a plugin only to add or change something.
Mini Challenge
Create a folder mini on your machine (anywhere outside your Git repositories), with a pom.xml containing coordinates com.example:mini:1.0-SNAPSHOT and the Java 21 properties from this lesson, and one class src/main/java/com/example/Mini.java that prints a line. Run mvn compile in that folder. If your Maven is not installed yet, check with mvn -v first and install it using the instructions on the Maven website; installation steps differ by operating system, so follow the official page for yours.
Check your work: the output ends with BUILD SUCCESS, and a target/classes/com/example/Mini.class file exists.
Summary & Next Lesson
Key takeaways
- Maven expects
pom.xmlat the root, code insrc/main/java, tests insrc/test/java, and writes all output intotarget/. - The three coordinates
groupId,artifactId,versionidentify a project (and every library). - A
pom.xmlhas amodelVersion, coordinates, optionalproperties,dependencies, and, when needed,build/plugins. - Always set the Java version with
maven.compiler.release. - Plugins do the real work; default ones are used without being declared.
Next lesson
The next lesson, "Dependencies and Maven Central", looks closely at the <dependencies> block: where the libraries come from, what scope means, and how to add one.
Self-check
- Where do test classes go in a Maven project?
- What are the three coordinates?
- Which folder can always be deleted safely?
- What does
maven.compiler.releasecontrol? - Do you have to declare the compiler plugin in your
pom.xml?
أظهر الإجابات
src/test/java, in the same package folders as the code they test.groupId,artifactId,version.target/.- The Java version used to compile the code.
- No. The default plugins are used automatically; you declare plugins only to add or change behavior.
References
- Apache Maven, Introduction to the POM. Supports the minimal POM (
modelVersion,groupId,artifactId,version), the defaultjarpackaging, and thegroupId:artifactId:versionform. - Apache Maven, Introduction to the Standard Directory Layout. Supports the roles of
src/main/java,src/test/java,src/main/resources,target, andpom.xml. - All build outputs and error messages shown were produced by Maven 3.9.11 on JDK 21 in a scratch project; plugin versions in the output are the defaults of that Maven version and may differ in yours. The meaning of
-SNAPSHOTas "under development" is common Maven usage that I did not confirm on a fetched page.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.