1. Overview
In this tutorial, we’ll look at declaring dependencies in a Gradle build script. For our examples, we’ll be using Gradle 6.7.
2. Typical Structure
Let’s start with a simple Gradle script for Java projects:
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter:2.3.4.RELEASE'
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.3.4.RELEASE'
}
As can be seen above, we have three code blocks: plugins, repositories, and dependencies.
First, the plugins block tells us that this is a Java project. Secondly, the dependencies block declares version *2.3.4.*RELEASE of the spring-boot-starter dependency needed to compile the project’s production source code. Additionally, it also states that the project’s test suite needs spring-boot-starter-test to compile.
The Gradle build pulls all dependencies down from the Maven Central repository, as defined by the repositories block.
Let’s focus on how we can define dependencies.
3. Dependency Configurations
There are different configurations in which we can declare dependencies. In this regard, we can choose to be more or less precise, as we’ll see later on.
3.1. How To Declare Dependencies
To start, the configuration has 4 parts:
- group – identifier of an organization, company, or project
- name – dependency identifier
- version – the one we want to import
- classifier – useful to distinguish dependencies with the same group, name, and version
We can declare dependencies in two formats. The contracted format allows us to declare a dependency as a String:
implementation 'org.springframework.boot:spring-boot-starter:2.3.4.RELEASE'
Instead, the extended format allows us to write it as a Map:
implementation group:``'org.springframework.boot', name: 'spring-boot-starter', version: '2.3.4.RELEASE'
3.2. Types of Configuration
Furthermore, Gradle provides many dependencies configuration types:
- api – used to make the dependencies explicit and expose them in the classpath. For instance, when implementing a library to be transparent to the library consumers’
- implementation – required to compile the production source code and are purely internal. They aren’t exposed outside the package
- compileOnly – used when they need to be declared only at compile-time, such as source-only annotations or annotation processors. They don’t appear in the runtime classpath or the test classpath
- compileOnlyApi – used when required at compile time and when they need to be visible in the classpath for consumers
- runtimeOnly – used to declare dependencies that are required only at runtime and aren’t available at compile time
- testImplementation – required to compile tests
- testCompileOnly – required only at test compile time
- testRuntimeOnly – required only at test runtime
We should note that the latest versions of Gradle deprecate some configurations like compile, testCompile, runtime, and testRuntime. At the time of writing, they’re still available.
4. Types of External Dependencies
Let’s delve into the types of external dependencies we encounter in a Gradle build script.
4.1. Module Dependencies
Basically, the most common way to declare a dependency is by referencing a repository. A Gradle repository is a collection of modules organized by group, name, and version.
As a matter of fact, Gradle pulls down the dependencies from the specified repository inside the repository block:
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter:2.3.4.RELEASE'
}
4.2. File Dependencies
Given that projects don’t always use automated dependency management, some projects organize dependencies as part of the source code or the local file system. Thus, we need to specify the exact location where the dependencies are.
For this purpose, we can use files to include a dependency collection:
dependencies {
runtimeOnly files('libs/lib1.jar', 'libs/lib2.jar')
}
Similarly, we can use filetree to include a hierarchy of jar files in a directory:
dependencies {
runtimeOnly fileTree('libs') { include '*.jar' }
}
4.3. Project Dependencies
Since one project can depend on another to reuse code, Gradle offers us the opportunity to do so.
Let’s say we want to declare that our project depends on the shared project:
dependencies {
implementation project(':shared')
}
4.4. Gradle Dependencies
In certain cases, such as developing a task or a plugin, we can define dependencies that belong to the Gradle version we are using:
dependencies {
implementation gradleApi()
}
5. buildScript
As we saw before, we can declare the external dependencies of our source code and tests inside the dependencies block. Similarly, the buildScript block allows us to declare the Gradle build’s dependencies, such as third-party plugins and task classes. Particularly, without a buildScript block, we can use only Gradle out-of-the-box features.
Below we declare that we want to use the Spring Boot plugin by downloading it from Maven Central:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.3.4.RELEASE'
}
}
apply plugin: 'org.springframework.boot'
Hence we need to specify the source from which we’ll download external dependencies because there isn’t a default one.
What’s described above is related to older versions of Gradle. Instead, in newer versions, it’s possible to use a more concise form:
plugins {
id 'org.springframework.boot' version '2.3.4.RELEASE'
}
6. Conclusion
In this article, we looked at Gradle dependencies, how to declare them, and the different configuration types.
Given these points, the source code for this article is available over on GitHub.