Java > Java Build Tools > Gradle > Dependency Management in Gradle

Gradle Dependency Management: Basic Example

This snippet demonstrates a basic build.gradle file showcasing dependency management in Gradle. It includes adding dependencies from Maven Central, a common repository. It provides a fundamental understanding of how to declare dependencies using the dependencies block and different dependency configurations like implementation, testImplementation and api.

build.gradle file

  • plugins { id 'java' }: Applies the Java plugin, which adds Java compilation, testing, and packaging tasks to your project.
  • group 'com.example': Defines the group ID for your project, which is part of the Maven coordinate system (group ID, artifact ID, version).
  • version '1.0-SNAPSHOT': Defines the version of your project. 'SNAPSHOT' indicates a development version.
  • repositories { mavenCentral() }: Specifies the Maven Central repository as the source for dependencies. Gradle will search this repository to resolve the declared dependencies.
  • dependencies { ... }: This block declares the dependencies your project needs. Each line within this block specifies a dependency and its configuration.
  • implementation 'org.apache.commons:commons-lang3:3.12.0': Declares a dependency on the Apache Commons Lang library. The implementation configuration means that this dependency is required for compiling the project and is also exposed to modules that depend on this project. Changes to implementation dependencies require recompilation of dependent modules.
  • testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1': Declares a dependency on JUnit Jupiter API for writing tests. testImplementation indicates that this dependency is only used for compiling tests.
  • testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1': Declares a dependency on JUnit Jupiter Engine, only required at runtime for executing the tests.
  • api 'com.google.guava:guava:31.1-jre': Declares a dependency on the Guava library. The api configuration means that this dependency is not only required for compilation but also exposed as part of the project's API. Changes to api dependencies require recompilation of any modules that depend on this project. This is less common than implementation and should be used when other modules need to use the classes from the dependency directly.
  • test { useJUnitPlatform() }: Configures the test task to use the JUnit Platform for running tests.

plugins {
    id 'java'
}

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

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.apache.commons:commons-lang3:3.12.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
    api 'com.google.guava:guava:31.1-jre'
}

test {
    useJUnitPlatform()
}

Concepts Behind the Snippet

This snippet illustrates the core concepts of dependency management: specifying external libraries that your project relies on. Gradle uses a declarative approach, where you define the dependencies in the build.gradle file, and Gradle automatically downloads and manages those dependencies. The implementation configuration is the most common and generally preferred for most dependencies because it hides internal dependencies from consumers of your library, improving modularity and reducing the risk of version conflicts. Use api judiciously when you need to expose dependencies as part of your public API.

Real-Life Use Case

Imagine you're building a web application and need to use a JSON processing library like Jackson. You would add Jackson as a dependency in your build.gradle file, and Gradle would handle downloading Jackson and all of its transitive dependencies, making them available for use in your project. This eliminates the need to manually download and manage JAR files.

Best Practices

  • Use implementation by default: Unless you specifically need to expose a dependency as part of your API, use the implementation configuration.
  • Declare specific versions: Avoid using version ranges (e.g., '3.+') as they can lead to unpredictable behavior. Specify the exact version you want to use.
  • Regularly update dependencies: Keep your dependencies up-to-date to benefit from bug fixes, performance improvements, and security patches.
  • Use dependency locking: Gradle provides dependency locking to ensure consistent builds across different environments and over time.

When to Use api vs. implementation

Use api only when your project's public API exposes types from the dependency. If the dependency is only used internally within your project's implementation, use implementation. Incorrectly using api can increase compile times and create unnecessary dependencies.

Alternatives

Alternatives to mavenCentral() are jcenter() (deprecated), or a company-hosted Maven repository. You could also use a local file based repository, though this is uncommon and should be avoided if possible.

Pros

The pros of using Gradle for dependency management include:

  • Automated dependency resolution: Gradle automatically downloads and manages dependencies.
  • Transitive dependency management: Gradle handles dependencies of your dependencies.
  • Configuration flexibility: Gradle offers various dependency configurations to control dependency visibility.

Cons

The cons of using Gradle for dependency management include:

  • Initial learning curve: Gradle's DSL can be complex for beginners.
  • Build file complexity: Large projects can have complex build.gradle files.

FAQ

  • What happens if I don't specify a version for a dependency?

    Gradle will attempt to resolve the dependency with a version that satisfies other dependencies in your project, which can lead to unpredictable results. It's best practice to always specify a version.
  • How can I exclude a transitive dependency?

    You can exclude a transitive dependency using the exclude keyword in the dependency declaration. For example: implementation('org.springframework:spring-webmvc:5.3.27') { exclude group: 'commons-logging', module: 'commons-logging' }