Maven — POM.xml, Build Lifecycle, Dependencies, Plugins, and Multi-Module Projects
In this tutorial, you will learn about Maven. We cover key concepts, practical examples, and best practices to help you master this topic.
Apache Maven is a build automation and project management tool that uses a Project Object Model (POM) for configuration. Unlike Ant (which required writing imperative build scripts), Maven is convention-based — it defines standard phases and plugins, so most builds require minimal configuration beyond listing dependencies.
What You'll Learn
- pom.xml structure and core elements
- The build lifecycle: phases and goals
- Dependency management: scope, exclusions, versioning
- Plugins for compilation, testing, and packaging
- Multi-module projects
Why It Matters
Maven is the de facto standard for Java builds. Understanding Maven is essential for contributing to open-source projects, working in enterprise environments, and managing dependencies without version conflicts.
Real-World Use
Every Spring Boot project, every library on Maven Central, and virtually every Java CI/CD pipeline uses Maven or Gradle.
pom.xml Structure
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>My Application</name>
<description>A sample Java application</description>
</project>
Coordinates
- groupId — reversed domain name (com.example, org.apache)
- artifactId — project name (spring-boot-starter-web)
- version — release number (1.0.0, 2.5.0-SNAPSHOT)
- packaging — jar, war, pom, ear
The Build Lifecycle
Maven has three built-in lifecycles: default, clean, site. The default lifecycle phases:
| Phase | Description |
|---|---|
validate |
Validate project structure |
compile |
Compile source code |
test |
Run unit tests |
package |
Build JAR/WAR |
verify |
Run integration tests |
install |
Copy artifact to local Repository |
deploy |
Copy artifact to remote repository |
mvn compile # compile only
mvn test # compile + test
mvn package # compile + test + package
mvn install # full build + copy to local repo
mvn clean install # clean + full build
Dependency Management
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Scopes
| Scope | Description |
|---|---|
compile |
Default — available everywhere |
provided |
Available at compile time, provided by runtime (e.g., Servlet API) |
runtime |
Not needed at compile time, needed at runtime |
test |
Available only for test compilation and execution |
system |
Similar to provided, but must include the JAR path |
Transitive Dependencies
Maven automatically includes dependencies of your dependencies. Use mvn dependency:tree to visualize:
mvn dependency:tree
Excluding Transitive Dependencies
<dependency>
<groupId>com.example</groupId>
<artifactId>library-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.example</groupId>
<artifactId>library-b</artifactId>
</exclusion>
</exclusions>
</dependency>
Plugins
Plugins add goals to lifecycle phases:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
Common Plugins
maven-compiler-plugin— compile with specific Java versionmaven-surefire-plugin— run unit testsmaven-failsafe-plugin— run integration testsmaven-jar-plugin— create JAR with manifestmaven-shade-plugin— create uber JAR with dependencies
Multi-Module Projects
Parent POM (pom.xml with packaging=pom):
<project>
<groupId>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>api</module>
<module>web</module>
</modules>
</project>
Child module (core/pom.xml):
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>core</artifactId>
</project>
Dependency Management in Parent
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Common Mistakes
- Not specifying Java version. The maven-compiler-plugin defaults to Java 5 or 8 depending on the version. Always set
<source>and<target>. - Committing IDE files. Use
.gitignoreto excludetarget/,.idea/, and*.imlfiles. - Forgetting dependency scope. Test libraries (JUnit, Mockito) should have
<scope>test</scope>to avoid shipping them in production. - Version conflicts. Use
mvn dependency:treeto debug conflicts. Use<dependencyManagement>in parent POM to centralize versions. - Slow builds without parallel mode. Use
mvn -T 4to compile modules in parallel.
Practice Questions
1. What is the default lifecycle phase if you run mvn without arguments?
mvn runs the default lifecycle up to the package phase (including compile, test, package).
2. What is the difference between test and provided scope?
test is available only during test compilation and execution. provided is available during compile but not packaged — the runtime environment provides it.
3. How do you exclude a transitive dependency?
Using <exclusions> in the dependency declaration.
4. What is the purpose of the dependencyManagement section?
It centralizes dependency versions for a multi-module project. Child modules can declare dependencies without specifying the version.
5. What does mvn dependency:tree do?
It prints the dependency tree, showing transitive dependencies and helping debug conflicts.
Challenge Question:
Create a multi-module Maven project with parent, core, and app modules. The core module depends on Apache Commons Lang3. The app module depends on core. Use dependencyManagement in the parent to manage versions. Add the maven-compiler-plugin with Java 21. Build, test, and package the application.
FAQ
Mini Project
Create a complete Maven project structure:
- Run
mvn archetype:generateor manually create the directory structure - Add dependencies for JUnit 5 and Apache Commons Lang3
- Write a simple utility class (
StringUtils.reverse(String)) and a unit test - Configure the
maven-compiler-pluginfor Java 21 - Build with
mvn clean package - Run the JAR:
java -jar target/my-app-1.0.0.jar - Examine the dependency tree with
mvn dependency:tree
What's Next
Maven is a powerful build tool, but Gradle offers a different approach with its Groovy/Kotlin DSL and incremental build system. Lesson 46 covers Gradle — build.gradle, Kotlin DSL, tasks, dependencies, and the Gradle wrapper.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro