Maven & Gradle

No serious Java project is built by hand. By the end of this lesson you'll write a pom.xml and a build.gradle , add pinned dependencies, run the Maven lifecycle, and know exactly when to reach for Maven versus Gradle.

Learn Maven & Gradle in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Before You Start

You should be comfortable with Unit Testing (build tools run your tests) and basic OOP project structure. A little XML familiarity helps for Maven, but it is not required.

What You'll Learn

🧱 The Big Idea — A Build Tool Is Your Project's Recipe

A build tool turns your source code into a runnable, shippable artifact. It fetches the libraries you need, compiles your code, runs your tests, and packages the result into a JAR — every time, the same way, on every machine.

💡 Analogy: A build tool is like a recipe with an automatic grocery service. The recipe (your pom.xml or build.gradle ) lists the ingredients (dependencies) and the steps (compile, test, package). The tool does the shopping for you — it downloads each ingredient and the ingredients those ingredients need (transitive dependencies) — then cooks the dish in the right order. Maven hands you a fixed recipe template; Gradle lets you write your own.

Before build tools, developers downloaded JARs by hand and fought "it works on my machine" bugs. Maven (2004) and Gradle (2012) ended that: declare what you need, and the tool produces an identical build for everyone.

1️⃣ Maven — Create, Build, and the Lifecycle

Maven is built on convention over configuration : put your code in src/main/java and your tests in src/test/java , and Maven just knows what to do. You drive it through lifecycle phases that always run in order.

Phase

Command

What it does

validate

mvn validate

Check the project is correct

compile

mvn compile

Compile source to target/classes

test

mvn test

Run the unit tests

package

mvn package

Bundle into a JAR/WAR

install

mvn install

Copy the JAR to your local ~/.m2 repo

The key rule: each phase runs every phase before it . So mvn package automatically validates, compiles, and tests first. Read the worked output below — you can see all four phases run from one command.

2️⃣ The pom.xml — Coordinates, Scopes, and Plugins

The pom.xml (Project Object Model) is Maven's recipe. Three things make it work:

🎯 Your Turn #1 — Finish the pom.xml

Fill in the artifactId, version, and the dependency scope. The expected result is in the comments.

3️⃣ Gradle — The Same Job, Written in Code

Gradle does everything Maven does, but the recipe is a real script ( build.gradle in Groovy, or build.gradle.kts in Kotlin) instead of XML. The payoff is brevity and speed.

plugins { ... } — turn on capabilities (the java plugin adds compile/test/jar tasks).

repositories { mavenCentral() } — where dependencies are downloaded from.

implementation '...' — a compile dependency, written on one line (Maven's compile scope).

testImplementation '...' — a test-only dependency (Maven's test scope).

tasks — the unit of work Gradle runs ( build , test , run , or your own).

A single implementation line replaces a seven-line XML block. Gradle is also typically 2-10x faster on repeat builds because it caches task outputs and keeps a daemon warm.

🎯 Your Turn #2 — Finish the build.gradle

Add the repository and the two dependency configurations. The expected result is in the comments.

4️⃣ Dependency Management & Transitive Dependencies

You rarely depend on just one library. When you add jackson-databind , it needs jackson-core and jackson-annotations — so the build downloads those too. Those are transitive dependencies : dependencies of your dependencies.

Sometimes two libraries pull in different versions of the same transitive dependency — a version conflict . Maven resolves it by "nearest wins" (the version closest to your project in the tree). To see and debug the full graph, use mvn dependency:tree or ./gradlew dependencies , then pin the version you want explicitly to force a single, predictable answer.

Mini-Challenge — Add a Library Yourself

No blanks this time — just the brief. Write the dependency lines from scratch, then verify with the dependency report.

Common Errors

Pro Tips

📋 Quick Reference — Maven vs Gradle

Task

Maven

Gradle

Config file

pom.xml (XML)

build.gradle (code)

Build (compile+test+jar)

./gradlew build

Run tests

./gradlew test

Install to local repo

./gradlew publishToMavenLocal

Clean output

mvn clean

./gradlew clean

Compile dependency

<scope>compile</scope>

implementation '...'

Test dependency

<scope>test</scope>

testImplementation '...'

Dependency tree

mvn dependency:tree

./gradlew dependencies

Frequently Asked Questions

🎉 Lesson Complete!

You can now write a pom.xml with GAV coordinates and scoped dependencies, run the Maven lifecycle ( validate → compile → test → package → install ), write a build.gradle with dependencies and tasks, read a transitive dependency tree, fix version conflicts, and choose Maven or Gradle for the job. That is the build-tool literacy every Java team expects.

Next up: Modular Java — the Java Platform Module System (JPMS).

Practice quiz

What are the three GAV coordinates that uniquely identify an artifact?

  • name, type, scope
  • repo, branch, tag
  • group, artifact, version
  • id, key, value

Answer: group, artifact, version. groupId (who), artifactId (what), and version (which release) form coordinates like com.mycompany:my-app:1.0.0.

What is the main difference between Maven and Gradle config files?

  • Maven uses XML (pom.xml), Gradle uses a script (build.gradle)
  • Maven uses code, Gradle uses XML
  • Both use YAML
  • Both use JSON

Answer: Maven uses XML (pom.xml), Gradle uses a script (build.gradle). Maven configures in XML (pom.xml) with strict conventions; Gradle uses a Groovy/Kotlin script, more concise and flexible.

Because each Maven phase runs every phase before it, what does 'mvn package' do first?

  • Nothing else
  • Only compile
  • install and deploy
  • validate, compile, then test

Answer: validate, compile, then test. Lifecycle phases run in order, so package automatically validates, compiles, and tests before packaging.

What is a transitive dependency?

  • A dependency you list twice
  • A dependency of your dependency that you never listed yourself
  • A test-only dependency
  • A dependency on the JDK

Answer: A dependency of your dependency that you never listed yourself. jackson-databind pulls in jackson-core and jackson-annotations automatically — those are transitive dependencies.

Which Maven scope puts a dependency only on the test classpath?

  • test
  • compile
  • runtime
  • provided

Answer: test. The test scope keeps a dependency (like JUnit) off the main classpath and out of the shipped JAR.

What is Gradle's equivalent of Maven's compile scope?

  • testImplementation
  • runtimeOnly
  • implementation
  • compileOnly

Answer: implementation. implementation '...' is a one-line compile dependency, replacing Maven's seven-line XML block.

Why should you always commit and use the wrapper (./gradlew or mvnw)?

  • It builds faster
  • It pins the exact build-tool version so everyone builds identically
  • It is required by Java 21
  • It skips tests

Answer: It pins the exact build-tool version so everyone builds identically. The wrapper pins the tool version for the whole team, avoiding 'works on my machine' build differences.

What does a -SNAPSHOT version mean?

  • An immutable release
  • A backup copy
  • A security patch
  • An in-progress build that can change at any time and is re-downloaded

Answer: An in-progress build that can change at any time and is re-downloaded. A -SNAPSHOT is a mutable, in-progress build; a release version like 1.2.0 is immutable for reproducible builds.

Why should you pin an exact dependency version like 2.17.0?

  • It downloads faster
  • So today's build is identical to next year's — LATEST or ranges can silently break it
  • It uses less disk
  • Maven requires it

Answer: So today's build is identical to next year's — LATEST or ranges can silently break it. Pinning an exact version makes builds reproducible; LATEST or open ranges let a new release break your build.

Which command prints the resolved transitive dependency tree in Gradle?

  • ./gradlew build
  • ./gradlew clean
  • ./gradlew dependencies
  • ./gradlew run

Answer: ./gradlew dependencies. ./gradlew dependencies prints the resolved graph (Maven's equivalent is mvn dependency:tree).

Continue this course

Related lessons