build.gradle Example

Gradle uses build.gradle as the central configuration file for a project. Written in the Groovy DSL, it defines plugins, dependencies, repositories, project metadata, and custom build logic. A typical Java, Android, or Spring Boot project can be configured with a relatively small script, while larger projects can use the same file to coordinate compilation, testing, packaging, and deployment tasks.

Kotlin DSL equivalents are usually stored in build.gradle.kts, but the configuration model remains largely the same.
The following example configures a standard Java application:

plugins {
    id 'java'
    id 'application'
}

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

repositories {
    mavenCentral()
}

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

application {
    mainClass = 'com.example.Main'
}

Each section has a clear purpose:

  • plugins applies plugins to the project. The java plugin enables Java compilation, testing, and JAR packaging, while application adds a runnable application task.
  • group and version identify the project and its published artifact.
  • repositories declares where Gradle should look for dependencies. mavenCentral() points to Maven Central.
  • dependencies lists external libraries and test tools.
  • application identifies the entry-point class used by tasks such as run.

After applying the application plugin, the application can be started with:

./gradlew run

Using the Gradle Wrapper is preferable to a system-wide Gradle installation because it keeps the build tool version consistent across developers and CI environments.

Understanding Dependency Configurations

Gradle’s dependency configurations determine which parts of a project can access a library. Choosing the correct configuration prevents unnecessary dependencies from leaking into downstream projects.

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

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'

    compileOnly 'org.projectlombok:lombok:1.18.30'
    annotationProcessor 'org.projectlombok:lombok:1.18.30'

    runtimeOnly 'com.mysql:mysql-connector-j:8.2.0'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

The most common configurations are:

  • implementation makes a dependency available during compilation and runtime, but does not expose it on the consumer’s compile classpath. This is the usual choice for application dependencies.
  • api exposes a dependency to projects that consume the current library. It requires the java-library plugin and should be reserved for types that appear in a library’s public API.
  • compileOnly provides a dependency during compilation without including it in the runtime artifact. It is commonly used for Lombok, though annotation processing must also be enabled as shown above.
  • annotationProcessor supplies annotation processors used at compile time.
  • runtimeOnly includes a dependency only when the application runs. Database drivers are a typical example.
  • testImplementation makes a library available to test source sets without adding it to the main application.

Keeping production and test dependencies separate reduces build size and avoids accidental use of test-only libraries in production code.

Spring Boot Configuration

Spring Boot projects usually combine the Spring Boot plugin with the dependency-management plugin:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.0'
    id 'io.spring.dependency-management' version '1.1.4'
}

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

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

The Spring Boot dependency-management plugin imports a dependency bill of materials supplied by the selected Spring Boot release. As a result, compatible versions of related libraries are selected automatically, and individual version numbers can usually be omitted.

The Java toolchain configuration tells Gradle to compile with Java 17. Toolchains are more precise than setting sourceCompatibility alone because they allow Gradle to locate or provision the required JDK.

For a Spring Boot application, the usual local startup command is:

./gradlew bootRun

Adding Custom Tasks

Build files can also extend Gradle with project-specific tasks. For example, the following task prints basic project information:

tasks.register('printInfo') {
    group = 'help'
    description = 'Prints the project name and version.'

    doLast {
        println "Project: ${project.name}, Version: ${project.version}"
    }
}

Run it with:

./gradlew printInfo

The group and description properties make the task easier to discover through ./gradlew tasks.

Existing tasks can also be customized:

tasks.named('build') {
    doLast {
        println 'Build completed successfully.'
    }
}

This example adds an action to the end of the standard build lifecycle. For production scripts, custom tasks should declare their inputs and outputs so that Gradle can cache them, skip unnecessary execution, and participate correctly in incremental builds.

Essential Gradle Commands

These commands cover most day-to-day development tasks:

  • ./gradlew build compiles the project, runs tests, and creates the distributable artifact.
  • ./gradlew clean removes files generated by previous builds.
  • ./gradlew test runs the test suite.
  • ./gradlew run starts an application configured with the application plugin.
  • ./gradlew bootRun starts a Spring Boot application.
  • ./gradlew dependencies displays the dependency tree and helps identify conflicting or unexpected versions.
  • ./gradlew tasks lists available tasks.
  • ./gradlew projects shows the structure of a multi-project build.

When diagnosing dependency problems, the more focused report is often more useful:

./gradlew dependencyInsight \
  --dependency guava \
  --configuration runtimeClasspath

This command shows which dependency introduced a particular library and which versions were selected.

Practical Recommendations

  1. Generate and commit the Wrapper. Run gradle wrapper once, then commit gradlew, gradlew.bat, and the files under gradle/wrapper. Team members and CI servers can then use the same Gradle version.

  2. Use modern dependency configurations. Replace legacy configurations such as compile and testCompile with implementation, api, and testImplementation.

  3. Centralize versions in larger builds. Multi-project projects can use a version catalog in gradle/libs.versions.toml, shared convention plugins, or root-project configuration to avoid duplicated version numbers.

  4. Keep secrets out of the build script. Do not hardcode repository credentials, API tokens, or passwords. Use environment variables, an encrypted secrets manager, or carefully protected properties in gradle.properties.

  5. Avoid unnecessary configuration. Prefer lazy task registration with tasks.register, use task inputs and outputs for cacheable work, and avoid broad actions that force Gradle to rebuild unchanged files.

  6. Review dependency changes. Run the dependency report before upgrading a major library, and check whether new transitive dependencies introduce security risks or conflicts.

With these fundamentals in place, build.gradle becomes more than a list of dependencies: it becomes a reproducible and extensible definition of how the project is compiled, tested, packaged, and delivered.