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:
| Aspect | Maven | Gradle |
|---|---|---|
| Build file | pom.xml (XML) | build.gradle (Groovy) or build.gradle.kts (Kotlin) |
| Model | Fixed lifecycle phases | Configurable task graph (a DAG) |
| Extensibility | Plugins, bound to phases | Plugins and ad-hoc tasks written inline |
| Incremental builds | Limited | First-class: up-to-date checks + build cache |
| Wrapper | optional | gradlew is the norm — pins the Gradle version |
| Verbosity | More boilerplate | Concise, 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 task — compileJava, 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 treeUsing ./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.
What to take from the run:
- The build declares 7 tasks, but you never list an order by hand — you ask for
buildand 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
compileJavaandprocessResourcesfirst, thenclasses,jar,compileTestJava,test, and finallybuild. A task only runs after every task it depends on, which is why compilation precedes packaging and tests precedebuild. classesis reached through two paths (viajarand viatest) yet appears once in the order — thedoneset 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; onlyjar,compileTestJava,test, andbuildshow(EXEC). This is Gradle's incremental build: tasks whose inputs are unchanged are skipped. - The final line reports
4 of 7tasks executed. Skipping unchanged work is exactly why a secondgradle buildis 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
When you run 'gradle build', how does Gradle decide the order in which tasks like compileJava, test, and jar execute?