In the previous topic, we started learning about adding external libraries to our application with Gradle. We discovered that the first step is to define a repository, followed by specifying the dependencies. We also learned how to choose and specify different types of repositories. This time, we will continue exploring the process of adding external libraries using Gradle. We will focus on understanding how to define dependencies in the build.gradle file.
Configurations
To add a new dependency to your project, first, you need to identify it by the following attributes: group, name and version. All these attributes are required when you use Maven compatible repositories. If you use other repositories, some attributes might be optional. For example, a flat repository only needs a name and a version.
There are several ways to find these attributes. Some library maintainers are nice enough to list them on their website or git repository. Also, you can just search for them in your preferred repository.
Maven Central search example
All the dependencies are grouped into a named set of dependencies called configurations. Each of them has different characteristics and determines the following points:
the availability of dependencies on building steps;
the need to include dependencies in the final build artifact;
the visibility of dependencies for programmers who use your project as a library.
The 'java' and 'kotlin' plugins add a number of these configurations to your project. There are four of them:
implementationconfiguration means that the dependency is available both at compile-time and at runtime, and it can't be exposed to people who use your compiled code as an external library in their own projects. This configuration is considered the default one.compileOnlyconfiguration is used to define dependencies that should only be available at compile-time, but you do not need them at runtime.runtimeOnlyis used to define dependencies that you need only during runtime, and not at compile time.apiconfiguration is similar toimplementation, but will be exposed to the programmers who use your compiled code as a library in their projects.
In an existing project, you may see compile and runtime configurations as well, but they are deprecated now. Consider using implementation and runtimeOnly instead.
There also exist the same configurations only with the test prefix (e.g. testImplementation). Since tests are compiled and run separately and are not included in the final JAR (as well as their dependencies), they have their own set of dependencies. It helps decrease the size of a JAR, which is especially useful in Android development.
Please, note, that at this moment, you do not need to understand everything about configurations. Usually, the type of dependency configuration is already specified when you copy dependency info from an online repository or a website. If you would like to read more about configurations, see the official Gradle docs.
Defining dependency
When we decided on what dependencies we want and settled on their configurations, we are ready to add them to our build.gradle, which is as simple as adding repositories.
dependencies {
// This dependency is used by the application.
implementation group: 'com.google.guava', name: 'guava', version: '28.0-jre'
// Use JUnit test framework only for testing
testImplementation 'junit:junit:4.12'
// It is only needed to compile the application
compileOnly 'org.projectlombok:lombok:1.18.4'
}Here, we add these three dependencies as an example:
Guava library, provides useful collections and utils for a project;
JUnit, used for testing purposes;
Lombok, modifies bytecode at compile time and isn't necessary anymore after compilation.
As you may have noticed, there are two ways of declaring dependencies: the one where we explicitly declare group, name, and version, and the one where we just list them separated by the colon. Both are perfectly fine and are up to your preferences. Note that Groovy syntax is flexible, and you can use either single or double quotes for the dependency string and optionally enclose it in parentheses. All the following declarations are equally valid:
// 1
implementation("com.google.guava:guava:28.0-jre")
// 2
implementation "com.google.guava:guava:28.0-jre"
// 3
implementation 'com.google.guava:guava:28.0-jre'
// 4
def guava_version = "28.0-jre"
implementation "com.google.guava:guava:$guava_version"After adding dependencies in the build.gradle file, you can use the libraries in your source code, but do not forget to import them. Gradle will automatically download the libraries from repositories when building the project.
Colorful world
Now, as an example of using external libraries, we take a look at a program that prints colored text messages.
1. In the dependencies section of the build.gradle file, we need to include JCDP library:
implementation group: 'com.diogonunes', name: 'JCDP', version: '4.0.1'2. And then import and use it inside the source code. Here are Java and Kotlin examples.
Java:
package org.hyperskill.gradleapp;
import com.diogonunes.jcdp.color.ColoredPrinter;
import com.diogonunes.jcdp.color.api.Ansi;
public class App {
public static void main(String[] args) {
ColoredPrinter printer = new ColoredPrinter
.Builder(1, false).build();
printer.print("Hello, colorful world!",
Ansi.Attribute.BOLD, Ansi.FColor.BLUE, Ansi.BColor.WHITE);
}
}Kotlin:
package org.hyperskill.gradleapp
import com.diogonunes.jcdp.color.ColoredPrinter
import com.diogonunes.jcdp.color.api.Ansi
fun main(args: Array<String>) {
val printer = ColoredPrinter.Builder(1, false).build()
printer.print("Hello, colorful world!",
Ansi.Attribute.BOLD, Ansi.FColor.BLUE, Ansi.BColor.WHITE)
}Both programs print the same colored message: Hello, colorful world!
If you want to run this application in the terminal, you have two options:
You can either run
gradle jarcommand to build the jar file and then download and put the JCDP library jar into the same directory with theapp.jar.After that, you can run the application in that directory, adding the library classes to the classpath:
java -cp "*" org.hyperskill.gradleapp.AppOr you can create a so called uber jar. Add these lines to your
build.gradlefile:
jar {
manifest {
attributes('Main-Class': 'org.hyperskill.gradleapp.App')
}
from {
configurations.runtimeClasspath
.findAll { it.name.endsWith('jar') }
.collect { zipTree(it) }
}
}Then run gradle jar to build the artifact. When it's done, run the following command to start the application:
java -jar app.jarConclusion
You've learned only the basics of dependency management, but this is enough to write programs with external libraries. As you may have noticed, Gradle is a very flexible tool for managing dependencies. It allows you to choose repositories where to download them and also to configure when to use dependencies: during compile-time, during runtime, or when testing, and so on. As an example, we examined a program that prints colored messages using an external library.