Gradle Tasks, Dependencies, and Plugins

3 minute read

Gradle’s power comes from its flexible task system, fine-grained dependency configurations, and rich plugin ecosystem. This chapter covers the features you’ll use daily.


Built-In Tasks

./gradlew tasks --all    # list everything

# Common tasks
./gradlew build          # compile + test + jar
./gradlew clean          # delete build/
./gradlew test           # run tests
./gradlew jar            # create JAR
./gradlew compileJava    # compile main sources
./gradlew dependencies   # show dependency tree

Custom Tasks

// Groovy DSL
tasks.register('hello') {
    group = 'custom'
    description = 'Prints a greeting'
    doLast {
        println 'Hello from Gradle!'
    }
}
// Kotlin DSL
tasks.register("hello") {
    group = "custom"
    description = "Prints a greeting"
    doLast {
        println("Hello from Gradle!")
    }
}
$ ./gradlew hello
> Task :hello
Hello from Gradle!

Task Dependencies

tasks.register('generateCode') {
    doLast { println 'Generating code...' }
}

tasks.register('compileGenerated') {
    dependsOn('generateCode')
    doLast { println 'Compiling generated code...' }
}

tasks.register('packageAll') {
    dependsOn('compileGenerated', 'jar')
    doLast { println 'Packaging everything...' }
}
$ ./gradlew packageAll
> Task :generateCode
Generating code...
> Task :compileGenerated
Compiling generated code...
> Task :jar
> Task :packageAll
Packaging everything...

Task Types

// Copy task
tasks.register('copyDocs', Copy) {
    from 'src/docs'
    into 'build/docs'
}

// Delete task
tasks.register('cleanLogs', Delete) {
    delete 'logs/'
}

// Exec task
tasks.register('runApp', JavaExec) {
    classpath = sourceSets.main.runtimeClasspath
    mainClass = 'com.myapp.App'
    args = ['--port=8080']
}

Dependency Configurations

Configuration Compile Classpath Runtime Classpath Exposed to Consumers Maven Equivalent
implementation ✅ ✅ ❌ compile
api ✅ ✅ ✅ compile
compileOnly ✅ ❌ ❌ provided
runtimeOnly ❌ ✅ ❌ runtime
testImplementation test only test only ❌ test
testRuntimeOnly ❌ test runtime ❌ test
dependencies {
    // Your code uses this — not exposed to downstream projects
    implementation 'com.google.guava:guava:33.2.1-jre'

    // Your code uses this AND downstream projects need it too
    api 'com.fasterxml.jackson.core:jackson-databind:2.17.1'

    // Available at compile time, not packaged (container provides)
    compileOnly 'jakarta.servlet:jakarta.servlet-api:6.1.0'

    // Not needed at compile time, needed at runtime
    runtimeOnly 'org.postgresql:postgresql:42.7.3'

    // Testing only
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.3'
    testImplementation 'org.mockito:mockito-core:5.12.0'
}

implementation vs api

Library A (uses api for jackson):
  └── jackson-databind (api)

App depends on Library A:
  └── Can see jackson-databind ✅ (because api)

Library B (uses implementation for guava):
  └── guava (implementation)

App depends on Library B:
  └── Cannot see guava ❌ (because implementation)

Use implementation by default. Use api only when consumers need the dependency.


Dependency Tree

./gradlew dependencies --configuration runtimeClasspath

Output:

runtimeClasspath
+--- com.google.guava:guava:33.2.1-jre
|    +--- com.google.guava:failureaccess:1.0.2
|    \--- com.google.errorprone:error_prone_annotations:2.28.0
+--- org.postgresql:postgresql:42.7.3
\--- com.fasterxml.jackson.core:jackson-databind:2.17.1
     +--- com.fasterxml.jackson.core:jackson-core:2.17.1
     \--- com.fasterxml.jackson.core:jackson-annotations:2.17.1

Version Catalogs (libs.versions.toml)

Centralized version management (Gradle 7.4+):

gradle/libs.versions.toml:

[versions]
junit = "5.10.3"
mockito = "5.12.0"
jackson = "2.17.1"
spring-boot = "3.3.0"

[libraries]
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }

[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }

build.gradle.kts:

dependencies {
    implementation(libs.jackson.databind)
    testImplementation(libs.junit.jupiter)
    testImplementation(libs.mockito.core)
}

Version catalogs are the modern way to manage versions in Gradle — like Maven’s BOM.


Applying Plugins

// Groovy DSL
plugins {
    id 'java'
    id 'application'
    id 'org.springframework.boot' version '3.3.0'
    id 'io.spring.dependency-management' version '1.1.5'
}
// Kotlin DSL
plugins {
    java
    application
    id("org.springframework.boot") version "3.3.0"
    id("io.spring.dependency-management") version "1.1.5"
}

Spring Boot Gradle Plugin

./gradlew bootRun       # run Spring Boot app
./gradlew bootJar       # create executable JAR

Publishing Artifacts

plugins {
    id 'java-library'
    id 'maven-publish'
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
            groupId = 'com.myapp'
            artifactId = 'my-library'
            version = '1.0.0'
        }
    }
    repositories {
        maven {
            url = uri('https://nexus.mycompany.com/repository/maven-releases/')
            credentials {
                username = findProperty('nexusUser')
                password = findProperty('nexusPass')
            }
        }
    }
}
./gradlew publish

Common Mistakes

Mistake Fix
Using api everywhere Use implementation by default — api leaks dependencies
Missing useJUnitPlatform() JUnit 5 tests won’t be discovered
Hardcoded versions in every module Use version catalogs (libs.versions.toml)
Not using --configuration with dependency tree Default shows everything — filter to runtimeClasspath

Key Takeaways

  1. Custom tasks with tasks.register — use dependsOn for ordering.
  2. implementation = private dependency. api = exposed to consumers.
  3. Version catalogs (libs.versions.toml) centralize version management.
  4. Spring Boot plugin adds bootRun and bootJar tasks.
  5. ./gradlew dependencies shows the full dependency tree.

What’s Next?

Next: Gradle vs Maven — a comprehensive comparison to help you choose the right tool.

Updated: