W3docs

Java Maven Introduction

What Maven is, how it builds Java projects, and how to install and run mvn commands.

Maven is the standard build automation and dependency management tool for Java. It turns a project into a declarative description — what it is, what it depends on, how it is built — and then runs the build for you. Instead of hand-managing JAR files and javac flags, you declare your needs in a single file and Maven downloads dependencies, compiles, tests, and packages your code through a predictable, repeatable lifecycle.

The POM: a project as data

Every Maven project is described by a pom.xml (Project Object Model). It identifies the project with three coordinates — groupId, artifactId, and version (together the "GAV") — and lists the libraries it needs. The same coordinate scheme names your project and every dependency, which is how Maven locates artifacts in a repository.

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>shop-app</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.release>21</maven.compiler.release>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>6.1.0</version>
    </dependency>
  </dependencies>
</project>

The <packaging> element tells Maven what to produce (jar, war, pom). The <properties> block holds reusable values — here, the Java release the compiler targets. The POM grows to cover plugins, profiles, and inheritance; the Maven POM chapter breaks those parts down.

Dependencies and the transitive classpath

You list only your direct dependencies. Maven reads each dependency's own POM and pulls in everything they need — the transitive dependencies — building the full classpath automatically. Declaring spring-web also brings in spring-core without you naming it.

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.17.0</version>
</dependency>

Each dependency has a scope that controls when it is on the classpath. The default, compile, makes it available everywhere; test limits it to test code; provided means the runtime supplies it. For version conflicts, exclusions, and dependencyManagement, see Maven dependencies.

ScopeOn compile classpathOn test classpathPackagedTypical use
compile (default)YesYesYesNormal libraries
providedYesYesNoServlet API, supplied at runtime
runtimeNoYesYesJDBC drivers
testNoYesNoJUnit, Mockito

The build lifecycle

Maven builds run through a fixed sequence of phases. The key phases of the default lifecycle are validate, compile, test, package, verify, install, and deploy. Running a phase runs every phase before it — mvn package first validates, compiles, and tests, then packages. You never call phases out of order.

mvn compile          # compile main sources
mvn test             # compile + run unit tests
mvn package          # compile + test + build the JAR/WAR
mvn install          # package + copy artifact into your local repository
mvn clean package    # delete target/ first, then build fresh

Each phase is bound to one or more goals supplied by plugins (for example compiler:compile is bound to the compile phase). Plugins are where the actual work lives; the lifecycle just orders it. The Maven build lifecycle chapter walks through all three built-in lifecycles and how goals attach to phases.

Repositories

Maven fetches artifacts from repositories. When you build, it looks first in your local repository (~/.m2/repository), a cache on your machine. On a miss it downloads from a remote repository — by default Maven Central — and stores a copy locally so the next build is offline-fast. Teams often add a private repository for internal libraries.

project pom.xml  →  local repo (~/.m2)  →  remote repo (Maven Central)
                     (cache hit?)            (download + cache)

A runnable example

The code runner has no Maven on its classpath, so the program below models Maven's core mechanics with plain JDK code: it stores a tiny POM as a map, resolves the transitive dependency closure (de-duplicating shared artifacts), and walks the build lifecycle, stopping at the requested phase. The shapes here — GAV coordinates, transitive resolution, ordered phases — are exactly what real Maven does.

java— editable, runs on the server

What to take from the run:

  • The project and every dependency are named by the same GAV triple, which is why Project coordinates: com.example:shop-app:1.0.0 reads just like a dependency line.
  • Only two dependencies are declared, but the resolved classpath has four JARs — spring-core and jackson-annotations were pulled in transitively, exactly as Maven does.
  • The seen set guarantees each artifact appears once; this is Maven's de-duplication that stops a shared library from landing on the classpath twice.
  • Executing: mvn package runs validate, compile, and test before package and then stops — running a phase always runs every earlier phase in order.
  • The build halts at package and never reaches install, mirroring how the requested goal bounds how far the lifecycle advances.

When to reach for Maven

Maven shines on conventional Java and JVM projects: its "convention over configuration" defaults (source in src/main/java, tests in src/test/java, output in target/) mean a new contributor can build any Maven project the same way. Its declarative XML is easy to read and diff, and IDE support is mature. The trade-off is verbosity and limited flexibility for unusual builds. Where you need scripted, highly customized builds, Gradle is the common alternative — it uses a programmable build script instead of declarative XML but covers the same ground: GAV coordinates, transitive dependencies, and a task (rather than phase) graph.

Next steps

Practice

Practice
In Maven, what happens when you run 'mvn package'?
In Maven, what happens when you run 'mvn package'?
Was this page helpful?