Maven Tutorial: Learn Build Tool from Scratch (2026)
Maven is the most widely used build tool in the Java ecosystem. Its convention-over-configuration approach, declarative POM files, and dependency management via Maven Central make it the default choice for enterprise Java projects. After maintaining multi-module Maven builds with hundreds of dependencies, I have learned that understanding the build lifecycle and dependency resolution mechanics is essential for avoiding the dreaded "jar hell".
This tutorial covers the POM structure, build lifecycle, dependency management, plugins, profiles, and best practices for multi-module projects.
POM Structure and Coordinates
The Project Object Model (POM) is an XML file describing your project. GroupId, artifactId, and version form the Maven coordinates that uniquely identify your artifact. The packaging element determines the output format — jar, war, pom, or ear. The parent element establishes inheritance from a parent POM for shared configuration.
Maven enforces a standard directory layout: src/main/java for sources, src/main/resources for resources, src/test/java for tests, and target/ for build output. Sticking to this convention lets new developers understand any Maven project immediately.
4.0.0
com.example
order-service
2.3.1
jar
Build Lifecycle and Phases
Maven has three built-in lifecycles: default (main build), clean (cleanup), and site (documentation). The default lifecycle phases execute in order: validate, compile, test, package, verify, install, deploy. Plugins bind goals to these phases; for example, the compiler plugin runs during compile and the surefire plugin during test.
Run mvn clean install to clean, compile, test, package, and install the artifact in your local repository. Use mvn verify to run integration tests without installing. Understanding phase ordering helps you diagnose why certain tasks execute — or do not execute.
# Typical commands
mvn clean # Delete target/
mvn compile # Compile sources
mvn test # Run unit tests (surefire)
mvn package -DskipTests # Create JAR without tests
mvn install # Install to local repository
mvn deploy # Deploy to remote repository
# Check effective POM
mvn help:effective-pom
Dependency Management
Dependencies are declared in the dependencies section with groupId, artifactId, and version. Maven resolves transitive dependencies automatically and uses a nearest-wins strategy for version conflicts. Use the dependencyManagement section in a parent POM to centralize version declarations for all child modules.
Exclude transitive dependencies that conflict with other libraries. Use provided scope for dependencies available at runtime (Servlet API), test scope for test-only libraries, and optional to prevent transitive propagation. Run mvn dependency:tree to visualize the full dependency graph.
com.fasterxml.jackson
jackson-bom
2.17.0
pom
import
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
Plugins and Goals
Maven functionality is plugin-based. The compiler plugin compiles Java code, surefire runs unit tests, failsafe runs integration tests, javadoc generates documentation, and shade creates fat JARs. Plugin configurations belong in the build.plugins section with parameters passed via configuration elements.
Complex builds may involve code generation — the jaxb2-maven-plugin generates Java classes from XSD schemas, protobuf-maven-plugin compiles .proto files. These plugins typically bind goals to the generate-sources phase to run before compilation.
org.apache.maven.plugins
maven-compiler-plugin
3.13.0
21
true
org.apache.maven.plugins
maven-shade-plugin
package
shade
Profiles for Environment-Specific Builds
Maven profiles activate different build configurations based on environment, JDK version, or explicit flags. A common pattern: a dev profile with relaxed checks and fast builds, a ci profile with strict linting, and a release profile that signs jars and deploys to Maven Central.
Activate profiles with -PprofileName, by setting the activeProfiles in settings.xml, or by detecting system properties. Profile-specific configuration overrides the base POM values. Keep profiles minimal to avoid an explosion of build paths.
production
true
false
org.apache.maven.plugins
maven-failsafe-plugin
**/*IT.java
# mvn clean verify -Pproduction
Multi-Module Projects
Multi-module projects let you build related artifacts (api, core, persistence, web) in a single reactor build. The parent POM with packaging pom lists child modules. Modules inherit from the parent and can reference each other as inter-module dependencies, which Maven resolves from the reactor before looking at the local repository.
Use the reactor to enforce build order: Maven sorts modules by dependency graph. Run specific modules with -pl (project list) and resume from a failing module with -rf. The -am flag also-makes dependencies of the listed modules.
com.example
my-app
1.0.0
pom
my-app-api
my-app-core
my-app-web
# Build all modules
mvn clean install
# Build only specific modules and their deps
mvn -pl my-app-web -am clean package
Frequently Asked Questions
How do I fix a version conflict in transitive dependencies?
Use mvn dependency:tree to find the conflict. Add an explicit dependency in your POM with the desired version (nearest-wins). Alternatively, use the
What is the difference between dependencyManagement and dependencies?
dependencies declares actual dependencies used by the project. dependencyManagement declares versions and scopes that child modules inherit — it does not add the dependency itself. Use dependencyManagement in a parent POM to enforce consistent versions.
How do I skip tests in Maven?
Use -DskipTests to skip unit tests but still compile them. Use -Dmaven.test.skip=true to skip both compilation and execution of tests. The first is useful for quick builds, the second when tests cannot compile.
What is the Maven local repository and where is it?
The local repository is a cache of downloaded artifacts located at ~/.m2/repository. Maven checks it first before downloading from remote repositories. Clear specific artifacts or the entire repository with rm -rf ~/.m2/repository to force fresh downloads.
Originally published on Ayodhyyya. Last updated June 1, 2026.