Dependencies

Dependencies #

Maven (like Gradle) acts both as a build automation tool and as a package manager.

As a package manager, Maven (like Gradle) relies by default a software repository called Maven Central. This repository contains more than 260.000 artifacts (more than 6 million when counting versions), with over a trillion downloads in 2023.

An artifact on Maven Central is identified by its coordinates.

Finding a library’s coordinates #

The coordinates of a library are often provided as part of its documentation. For instance, this is the case of Google’s Guava library.

Alternatively, this website can be used to search for the coordinates of an artifact on Maven Central (as well as 1990 other repositories). For instance, here are the coordinates for the latest version of the Smile library for machine learning.

Local repository #

By default, Maven caches project dependencies (as jars) in a hidden directory with path <homeFolder>/.m2 (or <homeFolder>\.m2 on Windows). In Maven’s terminology:

  • this directory is called the local repository,
  • an artifact is installed when it is added the local repository.

When a project is build, Maven searches for each dependency in the local repository. If the corresponding artifact is not installed, then it is downloaded (e.g. from Maven central) and installed.

Gradle proceeds in a similar way (but the cache is under <homeFolder>/.gradle).

Note. The local repository can contain different versions of the same artifact.

Note. By default, when installing an artifact, Maven also installs all its missing dependencies, transitively. Gradle proceeds in a similar way.

Warning. Maven (like Gradle) does not support cyclic dependencies, i.e. an artifact cannot depend on itself (directly or transitively).

Does this imply that the dependencies of an artifact form a tree?

No. A tree is a rooted acyclic graph, but the converse does not hold.

To display the dependency graph of a project (unraveled as a tree), run:

mvn dependency:tree

If a project depends (transitively) on two versions of the same artifact, then the “closest” version (in the dependency graph) is chosen (or the first declared in case of a tie).

Note. If a project depends on a SNAPSHOT version, then the installed artifact is automatically updated on a regular basis (by default once per day), by both Maven and Gradle.

Declaring a dependency #

Maven #

A dependency can be declared by adding the following to the pom.xml file, inside the <dependencies> tag:

<dependency>
    <groupId> XXX </groupId>
    <artifactId> XXX </artifactId>
    <version> XXX </version>
</dependency>

In addition, the attribute <scope>test<\scope> can be added to indicate that the dependency is needed for unit tests only (other scopes can be specified).

Example. Here is how to declare dependencies on Guava, Smile and JUNIT 5, while specifying that the latter is only needed for unit tests:


<dependencies>

  <dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.1.0-jre</version>
  </dependency>

  <dependency>
    <groupId>com.github.haifengl</groupId>
    <artifactId>smile-core</artifactId>
    <version>4.3.0</version>
  </dependency>

  <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>
  </dependency>

</dependencies>

Gradle #

There are several alternative syntaxes to declare a dependency with Gradle (these may depend on the project’s configuration).

Example. If you chose the board game as your project, then you can declare a dependency in the file core/build.gradle as follows. Within:

dependencies {
  ...
}

add a line:

    implementation group: XXX, name: XXX, version: XXX

For instance:

    implementation group: 'com.google.guava', name: 'guava', version: '33.1.0-jre'

To specify that the dependency is only needed for unit tests, replace implementation with testImplementation.

Using an external library #

Once a dependency is declared, the classes and interfaces of the corresponding library can be used in the source code of the project, with a regular import statement.

Example. Guava provides a convenient class ImmutableList for lists that cannot be modified. It can be used as follows:

import com.google.common.collect.ImmutableList;

public class MyClass {

    String name;
    ImmutableList<Integer> values;

    public MyClass(String name, ImmutableList<Integer> values){
        this.name = name;
        this.values = values;
    }
    ...
}

Hint. Import statements like the one above can be automatically generated by your IDE.