Gradle Dependency Management

Dependency management is a crucial aspect of any build tool, and Gradle provides a flexible and powerful way to manage dependencies. This guide covers the basics of dependency management in Gradle, including how to declare, resolve, and manage dependencies.

1. Declaring Dependencies

Syntax

Dependencies in Gradle are declared inside a dependencies block in the build.gradle file. Each dependency is specified with a group, name, and version.

Example

dependencies {
    implementation 'org.apache.commons:commons-lang3:3.12.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
}

In this example:

  • implementation is used for compile-time dependencies.
  • testImplementation is used for dependencies required only for testing.
  • testRuntimeOnly is used for dependencies required only at runtime during testing.

2. Dependency Configurations

Common Configurations

Gradle provides several configurations for different dependency scopes:

  • implementation: For dependencies required at compile and runtime.
  • api: For dependencies that should be exposed to consumers (for libraries).
  • compileOnly: For dependencies required only at compile time.
  • runtimeOnly: For dependencies required only at runtime.
  • testImplementation: For dependencies required only for tests.

Example

dependencies {
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2'
    compileOnly 'org.projectlombok:lombok:1.18.26'
    runtimeOnly 'mysql:mysql-connector-java:8.0.33'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
}

3. Transitive Dependencies

Gradle automatically resolves transitive dependencies. If a dependency has its own dependencies, those are included in your project as well.

Example

If you add spring-core, Gradle will also include its dependencies automatically.

dependencies {
    implementation 'org.springframework:spring-core:6.0.9'
}

4. Excluding Dependencies

Sometimes, you may need to exclude transitive dependencies that are not required or conflict with other dependencies.

Example

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web:3.0.5') {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
    }
}

In this example, the spring-boot-starter-tomcat module is excluded from the spring-boot-starter-web dependency.

5. Dependency Version Management

Gradle supports various ways to manage dependency versions, such as using properties or version catalogs.

Using Properties

You can define versions in a gradle.properties file.

gradle.properties

jacksonVersion=2.17.2
junitVersion=5.10.0

build.gradle

dependencies {
    implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
    testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
}

Using Version Catalogs

Version catalogs provide a centralized way to manage dependencies.

settings.gradle

dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('jackson', '2.17.2')
            version('junit', '5.10.0')

            library('jackson-databind', 'com.fasterxml.jackson.core', 'jackson-databind').versionRef('jackson')
            library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
        }
    }
}

build.gradle

dependencies {
    implementation libs.jackson.databind
    testImplementation libs.junit.jupiter.api
}

6. Dependency Management Example

Let's create a simple project to demonstrate dependency management.

Project Structure

gradle-dependency-example/
|-- build.gradle
|-- settings.gradle
|-- src/
    |-- main/
        |-- java/
            |-- com/
                |-- example/
                    |-- App.java
        |-- resources/
            |-- application.properties
    |-- test/
        |-- java/
            |-- com/
                |-- example/
                    |-- AppTest.java

build.gradle

plugins {
    id 'java'
    id 'application'
}

group 'com.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
}

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

settings.gradle

rootProject.name = 'gradle-dependency-example'

src/main/java/com/example/App.java

package com.example;

import com.fasterxml.jackson.databind.ObjectMapper;

public class App {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
        ObjectMapper mapper = new ObjectMapper();
        System.out.println("Jackson ObjectMapper: " + mapper);
    }
}

src/test/java/com/example/AppTest.java

package com.example;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class AppTest {
    @Test
    public void testApp() {
        assertTrue(true);
    }
}

7. Running the Project

Compile and Run

Use the following commands to build and run the project.

./gradlew build
./gradlew run

Conclusion

Gradle's dependency management is powerful and flexible. It allows you to easily manage project dependencies, resolve transitive dependencies, and manage versions. By understanding and utilizing these features, you can ensure that your projects are well-organized and maintainable.

In upcoming content, the latest versions of dependencies and plugins will be used to ensure compatibility and take advantage of the latest features and improvements.

Comments