Introduction to Maven and Gradle for Java Projects

Introduction Compiling a few classes with javac is easy. A real project, though, has hundreds of source files, tests, third-party libraries - each with its own dependencies - and has to be packaged the same way on every developer's machine and every build server. Build tools automate all of this, and in the Java world there are two main ones: Maven and Gradle. This tutorial is an introduction to Maven and Gradle for Java projects. We will start with what a build tool does, and the standard project layout both tools use. We will then build the same small application - which uses the Gson library - with Maven and with Gradle. Next we will understand dependencies and where they come from, and the commands you will use every day. Lastly, we will compare the two tools.

(Note - What a build tool automates is described in Compiling and Running Java Programs with javac and javaClick to read tutorial: Compiling and Running Java Programs with javac and java.) What does a build tool do?
  • Dependency management: you list the libraries you need, by name and version; the tool downloads them - and the libraries they need - from a repository such as Maven Central, and puts them on the classpath.
  • A standard build lifecycle: compile, run the tests, package a JAR, and more, with one command.
  • Reproducible builds: the build is described in a file kept with the code, so it works the same everywhere - on every machine, in CI, and in your IDE, which imports the project directly from that file.
  • Plugins: code formatting, test coverage, JavadocClick to read tutorial: Writing Javadoc Comments in Java with Examples generation, Docker images and much more.
The standard project layout Both Maven and Gradle use the same convention over configuration layout - if you put files where the tool expects them, there is nothing to configure -
greeter/
  pom.xml                     (Maven) or build.gradle + settings.gradle (Gradle)
  src/
    main/
      java/                   application source code, in package folders
        com/javabrahman/greeter/App.java
      resources/              configuration files, copied into the JAR
    test/
      java/                   test code - see the JUnit 5 tutorial
      resources/
  target/  (Maven) or build/  (Gradle)    generated output - never edit or commit it
Our application is the same for both tools. It uses Google's Gson library to turn a record into JSON -
src/main/java/com/javabrahman/greeter/App.java
package com.javabrahman.greeter;

import com.google.gson.Gson;

public class App {
  record Greeting(String name, String message, int year) { }

  public static void main(String[] args) {
    Greeting greeting = new Greeting("Tom", "Hello from a build tool!", 2026);
    String json = new Gson().toJson(greeting);
    System.out.println(json);
  }
}
Building with Maven Apache Maven describes a project in an XML file, pom.xml - the Project Object Model.
pom.xml
<?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.javabrahman</groupId>
  <artifactId>greeter</artifactId>
  <version>1.0</version>

  <properties>
    <maven.compiler.release>21</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.11.0</version>
    </dependency>
  </dependencies>
</project>
Explanation of the pom.xml
  • groupId, artifactId and version - the coordinates which identify this project, just as they identify every library in Maven Central.
  • maven.compiler.release - compiles for Java 21, like javac --release 21.
  • <dependencies> - the libraries the project needs, by their coordinates. Maven downloads Gson 2.11.0, and any libraries Gson itself depends on.
Maven builds in a fixed sequence of phases - the lifecycle: validate, compile, test, package, verify, install, deploy. Running a phase runs every phase before it, so mvn package compiles, runs the tests and creates the JAR. Here we also copy the dependencies into target/dependency, so that we can run the application with plain java -
mvn -q clean package dependency:copy-dependencies
dir /b target
java -cp "target/classes;target/dependency/*" com.javabrahman.greeter.App
 OUTPUT of the above commands
classes
dependency
generated-sources
greeter-1.0.jar
maven-archiver
maven-status
{"name":"Tom","message":"Hello from a build tool!","year":2026}
Explanation of the commands
  • -q (quiet) shows only errors; without it, Maven prints every phase and plugin it runs. clean deletes target first, for a fresh build.
  • target now holds the compiled classes, the JAR greeter-1.0.jar, and the copied gson JAR in dependency.
  • On macOS and Linux, use ls target, and : instead of ; in the classpath. On Java 24 and later, Maven 3.9 may print a warning about sun.misc.Unsafe; it comes from a library inside Maven, and does not affect the build.
Building with Gradle Gradle describes a build in code rather than XML - a Groovy script, build.gradle, or a Kotlin script, build.gradle.kts - and uses tasks instead of a fixed lifecycle. It is the standard build tool for Android, and is also widely used for server applications.
settings.gradle
rootProject.name = 'greeter'
build.gradle
plugins {
    id 'application'
}

group = 'com.javabrahman'
version = '1.0'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(24)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.google.code.gson:gson:2.11.0'
}

application {
    mainClass = 'com.javabrahman.greeter.App'
}
Explanation of the build.gradle
  • The application plugin adds tasks to compile, test, package and run a Java application whose main class is set in the application block.
  • The toolchain asks for Java 24 - Gradle finds a matching JDK on the machine, so everyone builds with the same Java version.
  • implementation '...' declares the same Gson dependency, in the short group:artifact:version form, and mavenCentral() says where to download it from.
Gradle's run task compiles the code and runs it with the dependencies on the classpath, in one step -
gradle -q run
 OUTPUT of the above command
{"name":"Tom","message":"Hello from a build tool!","year":2026}
The dependencies task shows the full tree of libraries the project uses, including those pulled in by Gson -
gradle -q dependencies --configuration runtimeClasspath
 OUTPUT of the above command
------------------------------------------------------------
Root project 'greeter'
------------------------------------------------------------

runtimeClasspath - Runtime classpath of source set 'main'.
\--- com.google.code.gson:gson:2.11.0
     \--- com.google.errorprone:error_prone_annotations:2.27.0

A web-based, searchable dependency report is available by adding the --scan option.
Gson 2.11.0 itself depends on Google's error_prone_annotations, which Gradle resolved automatically - a transitive dependency. Maven does exactly the same; mvn dependency:tree prints the equivalent tree. Where dependencies come from - and wrappers
  • Libraries are downloaded once, from Maven Central, into a local cache - ~/.m2/repository for Maven, ~/.gradle/caches for Gradle - and reused by every project.
  • Dependency scopes say where a library is needed: compile-and-run (Maven's default compile scope, Gradle's implementation), tests only (test / testImplementation), and others.
  • Wrappers - mvnw and gradlew scripts committed to the project - download the exact Maven or Gradle version the project needs, so contributors do not have to install the tool at all. Most real projects use them: ./gradlew build or ./mvnw package.
  • Search Maven Central at central.sonatype.com to find a library's coordinates and latest version.
Everyday commands
TaskMavenGradle
Compilemvn compilegradle compileJava
Run the testsmvn testgradle test
Build the JARmvn packagegradle build
Delete build outputmvn cleangradle clean
Dependency treemvn dependency:treegradle dependencies
Install to local repositorymvn installgradle publishToMavenLocal (with the maven-publish plugin)
Maven vs Gradle
  • Maven: declarative XML and a fixed lifecycle. Every Maven project looks the same, which makes it predictable and easy to learn. Very common in enterprise and Spring projects.
  • Gradle: build scripts in Groovy or Kotlin, flexible custom tasks, and fast incremental builds with a build cache and a background daemon. The standard for Android, and popular for large multi-project builds.
  • Both use the same repositories and dependency coordinates, and both are fully supported by IntelliJ IDEA, Eclipse and VS Code. Learn whichever your team uses - the concepts carry over.
For an older, IDE-based example of building a JAR with Maven, see How to Build a JAR using a Maven Project in EclipseClick to read how to build a JAR with Maven in Eclipse. Summary In this tutorial we first saw what a build tool does - dependency management, a standard build lifecycle, reproducible builds and plugins - and the standard src/main/java layout. We then built a small application which uses Gson with Maven, through its pom.xml and lifecycle phases, and with Gradle, through build.gradle and the run task. Next we looked at the dependency tree and transitive dependencies, local caches, scopes and wrappers. Lastly, we compared everyday commands and the two tools. In the next tutorial, we will write automated tests with JUnit 5Click to read tutorial: Unit Testing Basics with JUnit 5 in Java.