W3docs

Java Gradle Introduction

What Gradle is, how it compares to Maven, and how to set up a Gradle Java project.

Java Gradle Introduction

Gradle is a build automation tool for the JVM (and beyond) that compiles your code, runs your tests, manages your dependencies, and packages the result — all driven by a build script you write in Groovy or Kotlin. Where Maven describes a project with fixed XML, Gradle describes it with code: a build script that configures a graph of tasks. That shift to a programmable model, plus an incremental build cache that skips work it has already done, is why Gradle powers Android and many large Java projects.

Gradle vs. Maven

Both tools solve the same problem — repeatable builds with managed dependencies — but make different trade-offs. If you already know Maven, this is the map:

AspectMavenGradle
Build filepom.xml (XML)build.gradle (Groovy) or build.gradle.kts (Kotlin)
ModelFixed lifecycle phasesConfigurable task graph (a DAG)
ExtensibilityPlugins, bound to phasesPlugins and ad-hoc tasks written inline
Incremental buildsLimitedFirst-class: up-to-date checks + build cache
Wrapperoptionalgradlew is the norm — pins the Gradle version
VerbosityMore boilerplateConcise, but more "magic" to learn

Neither is strictly better. Maven's rigidity makes builds predictable; Gradle's flexibility makes complex builds expressible. This part teaches Gradle; the Maven part covers the other side.

A minimal Java build script

A Gradle Java project is one build.gradle plus a conventional source layout (src/main/java, src/test/java). Applying the built-in java plugin is what teaches Gradle how to compile, test, and jar:

plugins {
    id 'java'
}

group = 'com.example'
version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.google.guava:guava:33.0.0-jre'
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}

The Kotlin DSL (build.gradle.kts) expresses the same thing with type-safe accessors:

plugins {
    java
}

dependencies {
    implementation("com.google.guava:guava:33.0.0-jre")
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
}

Tasks are the unit of work

Everything Gradle does is a taskcompileJava, test, jar, build. Tasks declare dependencies on other tasks, forming a directed acyclic graph (DAG). When you run gradle build, Gradle walks that graph and runs each prerequisite exactly once, in order. You can also define your own:

task hello {
    doLast {
        println 'Hello from a custom Gradle task'
    }
}

task release {
    dependsOn 'build', 'hello'
}

The java plugin wires up a standard graph for you: classes depends on compileJava and processResources; jar depends on classes; test depends on classes and compileTestJava; and build depends on jar and test. Asking for build therefore runs all of them in dependency order.

The wrapper and the command line

The Gradle Wrapper (gradlew / gradlew.bat) is a checked-in script that downloads and runs the exact Gradle version a project expects, so contributors do not need Gradle installed globally:

./gradlew build          # compile, test, and package
./gradlew test           # run tests only
./gradlew clean build    # wipe outputs, then rebuild from scratch
./gradlew tasks          # list available tasks
./gradlew dependencies   # print the resolved dependency tree

Using ./gradlew instead of a system gradle is the recommended default — it makes the build reproducible across machines and CI.

A worked example: a task graph, resolved like Gradle does

There is no Gradle on this page's runner, so instead of a real build we model Gradle's core engine in plain Java: a graph of tasks with dependencies, resolved into an execution order with a topological sort — exactly what Gradle does when you type gradle build. The second pass shows how up-to-date tasks are skipped, which is Gradle's incremental-build trick.

java— editable, runs on the server

What to take from the run:

  • The build declares 7 tasks, but you never list an order by hand — you ask for build and the graph computes the rest. That inversion (declare dependencies, let the tool order them) is the heart of Gradle's task model.
  • The resolved order prints compileJava and processResources first, then classes, jar, compileTestJava, test, and finally build. A task only runs after every task it depends on, which is why compilation precedes packaging and tests precede build.
  • classes is reached through two paths (via jar and via test) yet appears once in the order — the done set guarantees each task executes a single time, just as Gradle never recompiles the same sources twice in one invocation.
  • On the second run, the three tasks marked up-to-date print (UP-TO-DATE) and are not counted; only jar, compileTestJava, test, and build show (EXEC). This is Gradle's incremental build: tasks whose inputs are unchanged are skipped.
  • The final line reports 4 of 7 tasks executed. Skipping unchanged work is exactly why a second gradle build is far faster than the first, and why the build cache matters on large projects.

What the rest of this part covers

Writing real build.gradle scripts, declaring and resolving dependencies from Maven Central, applying and configuring plugins, defining custom tasks, and using the wrapper in CI. The next chapter sets up a complete Gradle Java project from an empty directory.

Practice

Practice

When you run 'gradle build', how does Gradle decide the order in which tasks like compileJava, test, and jar execute?