Software projects rarely work in isolation. Projects often rely on reusable functionality from libraries. Some projects organize unrelated functionality into separate parts of a modular system.

Dependency management is an automated technique for declaring, resolving, and using functionality required by a project.

For an overview of dependency management terms, see Dependency Management Terminology.

Dependency Management in Gradle

dependency management resolution
Figure 1. Dependencies management at a glance

Gradle has built-in support for dependency management.

Let’s explore the main concepts with the help of a theoretical but common project:

  • This project builds Java source code.

  • Some Java source files import classes from the Google Guava library.

  • This project uses JUnit for testing.

The Gradle build file might look as follows:

build.gradle.kts
plugins {
    `java-library`
}

repositories { (1)
    google() (2)
    mavenCentral()
}

dependencies { (3)
    implementation("com.google.guava:guava:32.1.2-jre") (4)
    testImplementation("junit:junit:4.13.2")
}
build.gradle
plugins {
    id 'java-library'
}

repositories { (1)
    google() (2)
    mavenCentral()
}

dependencies { (3)
    implementation 'com.google.guava:guava:32.1.2-jre' (4)
    testImplementation 'junit:junit:4.13.2'
}
1 Here we define repositories for the project.
2 Here we declare remote and local repositories for dependency locations.

You can declare repositories to tell Gradle where to fetch local or remote dependencies.
In this example, Gradle fetches dependencies from the Maven Central and Google repositories.
During a build, Gradle locates and downloads the dependencies, a process called dependency resolution. Gradle then stores resolved dependencies in a local cache called the dependency cache. Subsequent builds use this cache to avoid unnecessary network calls and speed up the build process.

3 Here we define dependencies used by the project.
4 Here we declare the specific dependency name and version within a scope.

You can add code to your Java project from an external library such as com.google.common.base (a Guava package) which becomes a dependency.
In this example, the theoretical project uses Guava version 32.1.2-jre and JUnit 4.13.2 as dependencies.
A build engineer can declare dependencies for different scopes. For example, you can declare dependencies that are only used at compile time. Gradle calls the scope of a dependency a configuration.

Repositories offer dependencies in multiple formats. For information about the formats supported by Gradle, see dependency types.

Metadata describes dependencies. Some examples of metadata include:

  • coordinates for finding the dependency in a repository

  • information about the project that created the dependency

  • the authors of the dependency

  • other dependencies required for a dependency to work properly, known as transitive dependencies

You can customize Gradle’s handling of transitive dependencies based on the requirements of a project.

Projects with hundreds of declared dependencies can be difficult to debug. Gradle provides tools to visualize and analyze a project’s dependency graph (i.e. dependency tree). You can use a Build Scan™ or built-in tasks.

gradle core test build scan dependencies
Figure 2. Build scan dependencies report