When I learned Maven, everything felt tidy: one pom.xml, one fixed lifecycle, one set of rules. Then I opened an Android project, and the build file looked completely different. It had no XML, and it had words like plugins, implementation and tasks.register. Someone told me, "That's Gradle. It's like Maven, but it's actually code."
That sentence is the key to understanding Gradle. It solves the same problems as Maven (compile, test, package and download libraries), but it does it in a different way: your build file is a small program, and your build is a graph of tasks.
In this post, we'll learn what Gradle is, how it works, build.gradle.kts, the gradle init command, Gradle's terms, dependencies, tasks, repositories and the build lifecycle. At the end, there's a clear Gradle vs Maven comparison to help you choose. I've drawn the important pictures in text so you can see how the pieces fit.
If you've already read about Maven, Part 1 is a quick refresher, and you can skip ahead. I'm using Gradle 9.x with the Kotlin DSL. Gradle 9 needs Java 17 or newer to run.
Part 1: Build tools and why we need them (quick refresher)
A build tool automates the steps between your source code and a finished application. You describe the steps once, and the tool repeats them the same way every time.
Every Java project goes through the same journey:
source code byte code unit tests package build output
(.java) --> (.class) --> (JUnit) --> (zip it up) --> .jar or .war
| | | |
you write javac check that deployable
compiles the code works format
-
Compile:
javacturns.javafiles into.classbytecode. - Test: unit tests check that the code works.
- Package: the classes are bundled into one file. A JAR is for libraries and normal applications. A WAR is for web apps on a server like Tomcat.
Doing this by hand means downloading JAR files yourself, writing long javac commands and hoping that everyone on the team has the same setup. A build tool fixes that. The most useful part is dependency management: you say what libraries you need (name and version), and the tool downloads them, along with the libraries they need.
The three popular Java build tools are Ant (every step written by hand), Maven (XML and a fixed lifecycle) and Gradle (a script and a task graph). This post is about Gradle.
Part 2: What is Gradle?
Gradle is a build automation and dependency management tool. It builds Java, Kotlin, Android and many other kinds of projects. Instead of describing your project in XML, you write a build script in a real programming language:
-
Kotlin DSL:
build.gradle.kts(the default for new projects) -
Groovy DSL:
build.gradle
(A DSL, a domain-specific language, is a language shaped for one job. It looks like configuration, but underneath it's real code.)
What does Gradle do?
The same five jobs as Maven:
-
Project structure. It uses the same standard folders (
src/main/javaand so on). - Dependencies. It downloads the libraries you list, and what they need.
- Unit tests. It runs your tests (for example, with JUnit).
- Compile. It compiles your code.
- Package. It builds a JAR (or a WAR, with the right plugin).
It can also run your application, create distribution files and publish your library to a repository.
How does Gradle do it?
Gradle is built on three ideas:
1. Everything is a task. A task is one unit of work: compileJava, test, jar. Gradle's tasks can depend on other tasks. When you ask for one task, Gradle works out which other tasks must run first, and builds a graph (a map of who depends on whom).
2. Plugins add the tasks. Gradle itself is small. When you add the java plugin, it gives your project tasks like compileJava, test and jar, plus the standard folder layout and conventions.
3. It avoids repeating work. Each task declares its inputs (like source files) and outputs (like class files). If nothing changed since the last run, Gradle skips the task and marks it UP-TO-DATE. It also keeps a background process (the Gradle daemon) alive between builds so each build starts quickly.
When you run a build, Gradle goes through three phases:
./gradlew build
|
v
1. INITIALIZATION Find settings.gradle.kts. Decide which projects are part of the build.
|
v
2. CONFIGURATION Read the build scripts. Create the tasks. Build the task graph.
|
v
3. EXECUTION Run only the tasks that are needed, in dependency order.
Skip any that are UP-TO-DATE.
The Gradle Wrapper
Gradle projects almost always include a small helper called the wrapper: the files gradlew (Linux and macOS) and gradlew.bat (Windows). Instead of installing Gradle yourself, you run ./gradlew. The first time, it downloads the exact Gradle version the project needs. This means every developer and every build server uses the same version. Commit the wrapper files to your repository.
./gradlew --version
You'll see the Gradle version and the Java version it uses. Make sure the Java version is 17 or newer for Gradle 9.
The standard project structure
Gradle uses the same folder conventions as Maven:
my-app/
|-- settings.gradle.kts <- project name, and which sub-projects exist
|-- build.gradle.kts <- the build script
|-- gradlew, gradlew.bat <- the wrapper scripts
|-- gradle/wrapper/ <- wrapper files (commit these)
|-- src/
| |-- main/
| | |-- java/ <- your application code
| | +-- resources/ <- config files, images
| +-- test/
| |-- java/ <- your unit tests
| +-- resources/
+-- build/ <- created by Gradle: all build output
src/main is code that ships. src/test is code that only checks it. build/ is the output folder (the same idea as Maven's target/), and you can delete it any time.
(When you create a project with gradle init, the code usually goes inside an app/ folder, which is a sub-project, with its own build.gradle.kts. The layout inside it is the same.)
The build script: build.gradle.kts
Two small files describe a project. First, settings.gradle.kts gives it a name:
rootProject.name = "my-app"
Then build.gradle.kts describes how to build it:
plugins {
java
application
}
group = "com.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.11.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.withType<JavaCompile> {
options.release = 17
}
application {
mainClass = "com.example.App"
}
tasks.test {
useJUnitPlatform()
}
Here's what each part means:
-
plugins { ... }: which plugins to apply.javaadds compile, test and jar tasks.applicationadds the ability to run your program (aruntask). -
groupandversion: the project's coordinates. The name comes fromrootProject.name. -
repositories { ... }: where to download libraries from.mavenCentral()is the big public repository. You must declare it, because Gradle doesn't assume any repository. -
dependencies { ... }: your libraries. The word before the brackets (liketestImplementation) says when the library is needed. Theplatform(...)line imports a BOM (a list of matching versions), so the other JUnit lines don't need version numbers. -
options.release = 17: the Java version to compile for. Set it to your JDK version, like 17 or 21. -
application { mainClass }: which class hasmain. -
tasks.test { useJUnitPlatform() }: tells the test task to use JUnit 5. Without this line, JUnit 5 tests don't run.
The junit-platform-launcher line is needed with Gradle 9 so that tests can start.
The same thing in Groovy
If you see build.gradle (no .kts), it's the Groovy version. It looks a bit lighter, with fewer brackets and quotes:
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
}
Both do the same job. Kotlin DSL is the default for new projects, because IDEs can autocomplete it and show errors as you type.
Behind the scenes: the script is code
When Gradle runs build.gradle.kts, it compiles and runs it like a program. Lines like dependencies { ... } are real function calls, and the block in curly braces is code. That's why you can use variables, if statements and loops in a build script, which isn't possible in a plain XML file. It's powerful, but it also means a build script can become complicated if you aren't careful.
Creating a project: gradle init
Maven has archetype:generate. Gradle has gradle init. Here's the full command:
gradle init \
--type java-application \
--dsl kotlin \
--test-framework junit-jupiter \
--package com.example \
--project-name my-app \
--java-version 17
(On Windows Command Prompt, write it on one line without the \ characters.)
Let's break it down:
-
gradle init: runs the built-ininittask, which creates a new project. -
--type java-application: the kind of project.java-applicationis a command-line app. Other choices includejava-library,kotlin-application,groovy-applicationand more. -
--dsl kotlin: which language to write the build scripts in (kotlinorgroovy). -
--test-framework junit-jupiter: which test library to set up.junit-jupitermeans JUnit 5. Others includejunit(JUnit 4),testngandspock. -
--package com.example: the Java package for the sample classes. -
--project-name my-app: the name of the project. It defaults to the folder name. -
--java-version 17: the Java version to use in the project.
If you run plain gradle init with no options, Gradle asks you questions one by one (type, language, DSL, test framework), so it works interactively too. To see every option, run gradle help --task init.
The result is a working project with a sample App.java, a sample test, the build scripts and the wrapper files. A handy extra: if you run gradle init inside an existing Maven project, Gradle can convert the pom.xml into Gradle build files for you.
Note that gradle init needs Gradle installed once. After that, the generated project carries its own wrapper, so everyone else just uses ./gradlew.
Gradle terminology
Gradle's words are a little different from Maven's. Here they are side by side.
Project, plugin and task
- A project is something Gradle builds. A big build can have a root project and several sub-projects (modules).
- A plugin adds tasks, folders and conventions to a project (
java,application,war). - A task is one unit of work (
compileJava,test,jar).
Coordinates: group, name, version
Gradle uses the same coordinate system as Maven, so both tools can share libraries:
com.google.code.gson : gson : 2.11.0
group name version (Maven calls the middle part "artifactId")
-
group: who it belongs to, usually a reversed domain:com.example. -
name: the project name from
rootProject.name. -
version: the version, like1.0-SNAPSHOT.
Version: SNAPSHOT vs release
The same convention applies: a version ending in -SNAPSHOT means work in progress, and a version without it (like 1.0.0) is a finished release that should never change once published.
Gradle also allows "dynamic" versions like 2.+ or latest.release, which pick whatever is newest. Avoid them, because your build can change without you touching it.
Packaging: there's no <packaging> tag
Maven has a packaging setting (jar, war). Gradle doesn't. Instead, the plugin decides what gets built:
- the
javaplugin gives you ajartask - the
warplugin gives you awartask, for web applications - the
applicationplugin addsrun, plus tasks to build a runnable ZIP or TAR of your app - the
maven-publishplugin adds the tasks to publish your result
Templates: archetype vs init type
Maven's archetype is a project template. In Gradle, the same idea is the --type option of gradle init.
Gradle dependencies
A dependency is declared inside dependencies { }, using the format "group:name:version":
dependencies {
implementation("com.google.code.gson:gson:2.11.0")
}
Compare that to Maven, where the same dependency takes six lines of XML. Now you can use it right away:
import com.google.gson.Gson;
import java.util.Map;
public class App {
public static void main(String[] args) {
Gson gson = new Gson();
System.out.println(gson.toJson(Map.of("name", "Asha")));
}
}
Output:
{"name":"Asha"}
Configurations: when is the dependency needed?
In Maven, you use a scope. In Gradle, the word before the brackets is called a configuration. The common ones:
-
implementation: needed to compile and run your code. It's packaged with your app. This is the default choice. -
api: likeimplementation, but also shows the library to anyone who uses your library. It's available only with thejava-libraryplugin. -
compileOnly: needed to compile, but not packaged (the server provides it at run time). -
runtimeOnly: not needed to compile, but needed when running (like a database driver). -
testImplementationandtestRuntimeOnly: only for tests, like JUnit. -
annotationProcessor: for tools that run during compilation.
(Older Gradle versions used compile and testCompile. They were removed. If you see them in an old tutorial, use implementation and testImplementation instead.)
Transitive dependencies and version conflicts
As in Maven, if you add library A, and A needs B, Gradle fetches B too. See the whole tree with:
./gradlew dependencies
To find out why a specific library is there, use:
./gradlew dependencyInsight --dependency gson
When two libraries need different versions of the same third library, Gradle's default is simple: the highest version wins. (Maven picks the version nearest to your project. This is a real difference, and it's covered in the comparison below.)
my-app
|-- lib-a 1.0
| +-- lib-b 2.0
+-- lib-c 1.0
+-- lib-b 1.5
Gradle picks lib-b 2.0, the highest.
Maven would pick lib-b 2.0 here too, but only because lib-a was declared first.
For larger projects, many teams keep all versions in one file, gradle/libs.versions.toml, called a version catalog. Then every module refers to a library by a short name, and the versions are in one place.
Gradle tasks
A task is a named piece of work. When you apply the java plugin, you get a family of tasks:
-
clean: deletes thebuildfolder. -
compileJava: compiles the main code. -
processResources: copies the files fromsrc/main/resources. -
classes: a grouping task that means "main code is compiled and ready." -
compileTestJava: compiles the test code. -
test: runs the unit tests. -
jar: bundles the classes into a JAR. -
assemble: builds all the outputs (like the JAR), without running tests. -
check: runs all the verification (like the tests). -
build: does bothassembleandcheck.
With the application plugin, you also get run (starts your program). And with maven-publish, you get publishToMavenLocal and publish.
The commands you'll use most:
-
./gradlew clean: delete old output. -
./gradlew compileJava: compile only. -
./gradlew test: compile and run the tests. -
./gradlew build: compile, test and package. -
./gradlew run: run the application. -
./gradlew tasks: list all available tasks (add--allto see more).
(On Windows, use gradlew.bat instead of ./gradlew.)
Here is a trimmed ./gradlew build:
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :jar
> Task :assemble
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
> Task :check
> Task :build
BUILD SUCCESSFUL in 2s
Each > Task line is one task running, in dependency order. Now run it again without changing anything:
> Task :compileJava UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar UP-TO-DATE
> Task :assemble UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :test UP-TO-DATE
> Task :check UP-TO-DATE
> Task :build UP-TO-DATE
BUILD SUCCESSFUL in 400ms
Gradle noticed that nothing changed, so it skipped the work. That's the "avoid repeating work" idea in action. NO-SOURCE means the task had no input files, such as an empty resources folder.
The task graph behind build
Gradle doesn't run a fixed list of steps. It works backwards from the task you ask for and builds a graph:
build
|-- assemble
| +-- jar
| +-- classes
| |-- compileJava
| +-- processResources
+-- check
+-- test
|-- classes (same tasks, run only once)
+-- testClasses
|-- compileTestJava
+-- processTestResources
When you ask for test, Gradle runs only compileJava, processResources, classes, compileTestJava, processTestResources, testClasses and test. It doesn't build the JAR, because test doesn't need it.
Testing with JUnit
Create src/main/java/com/example/Calculator.java:
package com.example;
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
And src/test/java/com/example/CalculatorTest.java:
package com.example;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class CalculatorTest {
@Test
void addsTwoNumbers() {
assertEquals(5, new Calculator().add(2, 3));
}
}
./gradlew test
BUILD SUCCESSFUL in 2s
Gradle is quiet when everything passes. Now break the test on purpose by changing 5 to 6:
CalculatorTest > addsTwoNumbers() FAILED
org.opentest4j.AssertionFailedError at CalculatorTest.java:9
1 test completed, 1 failed
> Task :test FAILED
BUILD FAILED in 1s
The build fails, and the report is saved in build/reports/tests/test/index.html.
Here's a difference from Maven worth noticing. In Maven, a failing test stops the lifecycle before package, so no JAR is built. In Gradle, the jar task doesn't depend on test, so with ./gradlew build the JAR may already exist even though the build failed. The overall result is still BUILD FAILED, so don't ship it. If you publish a library, make sure the publishing task only runs after check has passed.
Running your application and your JAR
With the application plugin, running is one command:
./gradlew run
Hello World!
(This is the output of the sample App from gradle init. Yours prints whatever your main method prints.)
The JAR from the jar task doesn't say which class has main, so this fails:
java -jar build/libs/my-app-1.0-SNAPSHOT.jar
no main manifest attribute, in build/libs/my-app-1.0-SNAPSHOT.jar
You can fix that by adding the main class to the manifest:
tasks.jar {
manifest {
attributes["Main-Class"] = "com.example.App"
}
}
If your app uses outside libraries, you need a "fat JAR" that bundles them. The popular Shadow plugin does that. Alternatively, the application plugin's installDist task creates a folder with your JAR, its libraries and a start script in build/install/.
Gradle repositories
A repository is a storage place for libraries, organized by coordinates. Gradle uses the same repositories as Maven, so everything on Maven Central works. You list them in the repositories { } block, and Gradle searches them in the order you wrote them:
repositories {
mavenCentral()
maven {
url = uri("https://repo.mycompany.com/maven")
}
}
The common ones:
-
mavenCentral(): the huge public repository on the internet. This is where most open-source libraries live. -
maven { url = ... }: any other Maven-style server, such as your company's private repository. -
google(): Google's repository, used mostly for Android. -
mavenLocal(): the~/.m2/repositoryfolder that Maven uses. Use it only when you really must, because it can make builds depend on what's on your machine.
Gradle also keeps its own cache of downloaded libraries, in ~/.gradle/caches. Each library is downloaded once, and later builds reuse it. That's the same idea as Maven's local repository, in a different folder.
Need gson 2.11.0
|
v
1. Gradle's cache (~/.gradle/caches) --- found? ---> use it (no internet needed)
| not found
v
2. Repositories, in the order you declared them
(company server, mavenCentral, ...) --- found? ---> download it
| |
| v
+--------------------------------------------> save it in the cache
To publish your own library, apply the maven-publish plugin. publishToMavenLocal copies it into ~/.m2 (like Maven's install), and publish uploads it to a remote repository (like Maven's deploy). You configure the target address and login in the build script.
The Gradle build lifecycle
This is where Gradle is most different from Maven, so let's be careful.
Maven has fixed lifecycles (default, clean, site), each with named phases that always run in the same order.
Gradle has no fixed phase list. It has two different ideas:
1. The three build phases (we saw these earlier): initialization, configuration and execution. They describe how Gradle runs any build, whatever tasks you ask for.
2. The task graph. The order in which tasks run comes from the dependencies between tasks. A plugin says "test needs classes," and Gradle works out the rest.
To give you the same comfort as Maven's phases, the java plugin provides a few lifecycle tasks: grouping tasks that do no work themselves, but depend on others:
assemble = build everything (jar and other outputs), no tests
check = run all verification (tests, and other checks)
build = assemble + check
Here's how Maven's ideas map across:
MAVEN GRADLE
default lifecycle the task graph for the tasks you ask for
validate (settings and configuration phases)
compile compileJava
test test
package jar (part of "assemble")
verify check
install publishToMavenLocal
deploy publish
clean lifecycle (mvn clean) the "clean" task (./gradlew clean)
site lifecycle (mvn site) no direct equivalent: use the "javadoc" task and report plugins
The big difference: in Maven you run a phase and everything before it runs. In Gradle you run a task and only the tasks it depends on run.
What's inside build/ after a build?
build/
|-- classes/java/main/ <- compiled main code
|-- classes/java/test/ <- compiled test code
|-- libs/ <- the packaged JAR
|-- reports/tests/test/ <- the test report (open index.html)
+-- test-results/ <- raw test results
Gradle vs Maven: the comparison
Now the part that most people want. Both tools build Java projects, use the same repositories and the same library coordinates. The differences are in how you describe the build and how the build runs.
The build file
-
Maven:
pom.xml, which is XML. It's declarative: you describe what the project is, not what to do. It's verbose, but predictable, and every Maven project looks alike. -
Gradle:
build.gradle.kts(orbuild.gradle), which is code. It's shorter and you can use variables and logic, but a complex script can be harder to read.
The same single dependency:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
implementation("com.google.code.gson:gson:2.11.0")
The build model
- Maven: a fixed lifecycle of phases. Easy to learn: run a phase, and everything before it runs.
- Gradle: a graph of tasks. More flexible: you can add your own tasks, connect them anywhere and run only what's needed.
Speed
-
Gradle is built to skip work. It uses incremental builds (
UP-TO-DATEchecks), a long-running daemon, a build cache that can reuse results (even from other machines) and parallel execution. On large projects with many modules, this often makes Gradle noticeably faster, especially on repeated builds. -
Maven has also gained speed features (for example, parallel builds with
-T, and an optional daemon tool calledmvnd), and it does the same work each time, which keeps it simple. - For a small project, you'll hardly notice a difference. The gap grows with project size. Always measure your own project instead of trusting general claims.
Flexibility vs convention
- Maven strongly prefers convention. If you need something unusual, you usually write or configure a plugin, which takes effort. The benefit is that it's hard to make a build too clever.
- Gradle makes custom logic easy: you can write a task right in the build script. The risk is the opposite: it's easy to make a build too clever.
Dependencies
-
Scopes vs configurations: Maven has a few scopes (
compile,provided,runtime,test). Gradle has more fine-grained configurations (implementation,api,compileOnly,runtimeOnly,testImplementation). -
apivsimplementation: Gradle can hide a library's internal dependencies from the projects that use it (implementation), and this can cut compile time in big builds. Maven has no direct equivalent. - Version conflicts: Maven's default is "nearest to your project wins." Gradle's default is "highest version wins."
Maven scopes and their Gradle equivalents:
MAVEN SCOPE GRADLE CONFIGURATION
compile implementation (or api, in a java-library)
provided compileOnly
runtime runtimeOnly
test testImplementation / testRuntimeOnly
Learning curve
- Maven is easier to start with and easier to predict. Most Maven builds look nearly the same, so you can read an unfamiliar project quickly.
- Gradle has more concepts to learn (tasks, configurations, plugins, the DSL, the three phases). Once you know them, you can do more, but the first weeks are steeper.
Ecosystem and tooling
- Both are well supported in IntelliJ IDEA, Eclipse and VS Code, and both work with Spring Boot.
- Gradle is the standard build tool for Android. It's also popular in Kotlin projects.
- Maven is extremely common in enterprise and older Java projects, with a vast number of ready-made plugins.
- Both can use the wrapper (
gradlewandmvnw) so that every machine uses the same tool version.
Command cheat sheet
MAVEN GRADLE
mvn -v ./gradlew --version
mvn archetype:generate gradle init
mvn clean ./gradlew clean
mvn compile ./gradlew compileJava
mvn test ./gradlew test
mvn package ./gradlew build (or ./gradlew jar)
mvn install ./gradlew publishToMavenLocal
mvn deploy ./gradlew publish
mvn dependency:tree ./gradlew dependencies
mvn package -DskipTests ./gradlew build -x test
mvn test -Dtest=FooTest ./gradlew test --tests FooTest
mvn -o ./gradlew --offline
mvn -U ./gradlew --refresh-dependencies
pom.xml build.gradle.kts + settings.gradle.kts
target/ build/
~/.m2/repository ~/.gradle/caches
Note that ./gradlew build is the closest match to Maven's verify, because it also runs the checks. To package only, with no tests, use ./gradlew jar or ./gradlew assemble.
Which one should I use?
- Starting a new Android project? Gradle. It's the standard.
- Joining an existing project? Use whatever it already uses. Don't switch just because you like the other one.
- Learning Java build tools for the first time? Start with Maven if you want the simplest, most predictable model. Learn Gradle next. The concepts (coordinates, repositories, dependencies, plugins) carry over almost completely.
- Large multi-module project with slow builds? Gradle's incremental builds and caching can help a lot.
- Need a lot of custom build logic? Gradle makes it easier.
- Want every project to look the same, with few surprises? Maven.
- Building with Spring Boot? Either works well. Pick the one your team knows.
-
Want to move from Maven to Gradle? Run
gradle initinside the Maven project. It can convert the build for you. Then check the result by hand.
A sensible attitude: both are good tools, and the "right" one is usually the one your team already uses well.
Common mistakes
-
Running
gradleinstead of./gradlew. The globally installed Gradle may be a different version from the one the project needs. Use the wrapper. On Linux and macOS, if you get "permission denied," runchmod +x gradlew. -
Running Gradle on an old Java. Gradle 9 needs Java 17 or newer to run. Check
./gradlew --versionand yourJAVA_HOME. -
Using removed configurations.
compileandtestCompileno longer exist. UseimplementationandtestImplementation. -
Forgetting
useJUnitPlatform(). Without it, JUnit 5 tests aren't found, and the build "passes" with no tests run. Also remember thejunit-platform-launcherline for Gradle 9. -
Forgetting the repositories block. If you don't declare
mavenCentral(), you get "Could not resolve" errors, because Gradle assumes no default repository. -
Unpredictable versions. Dynamic versions like
2.+orlatest.release, and heavy use ofmavenLocal(), make builds change from day to day or machine to machine. Pin exact versions.
Quick recap
- Gradle automates building Java (and other) projects: compile, test, package and dependency management.
- Build scripts are code:
build.gradle.kts(Kotlin) orbuild.gradle(Groovy), plussettings.gradle.kts. - Gradle works with tasks in a graph, added by plugins. It skips tasks that are UP-TO-DATE, and it uses a daemon and caches to stay fast.
- A build has three phases: initialization, configuration and execution.
- Always use the wrapper (
./gradlew) so everyone uses the same Gradle version. - The standard layout is the same as Maven:
src/main/java,src/test/java, with output inbuild/. -
gradle init --type java-application ...creates a project, like Maven'sarchetype:generate. - Coordinates are group:name:version. There's no
packagingtag: plugins decide what is built. - Dependencies use configurations:
implementation,api,compileOnly,runtimeOnly,testImplementation. In conflicts, the highest version wins. - You must declare repositories, usually
mavenCentral(). Gradle caches downloads in~/.gradle/caches. - Key tasks:
clean,compileJava,test,jar,assemble,check,buildandrun. - Compared with Maven: Gradle is code, a task graph and often faster on big builds. Maven is XML, a fixed lifecycle and simpler to predict.
Try it yourself
Here's a small practice idea: run gradle init with the command from this post, then run ./gradlew build twice and watch the second run show UP-TO-DATE. Add the Calculator class and CalculatorTest, run ./gradlew test, then break the test on purpose and read the failure report in build/reports/tests/test/index.html. Finally, add the Gson dependency, print some JSON in App, and run it with ./gradlew run.
For a bonus experiment, run ./gradlew dependencies and ./gradlew tasks --all, and see how much Gradle shows you about your own project. If you already have a Maven project, copy it, run gradle init inside the copy, and compare the generated build.gradle.kts with the original pom.xml.
If this helped, or if you spotted something I should fix, tell me in the comments. I'm still learning too.
Top comments (0)