Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Adding Maven Dependencies and Build Plugins

Hello! Last lesson, you saw how pom.xml identifies a Maven project and how src/main/java holds its application code. Now you’ll make two practical additions to the POM: a library dependency that your Java code can use, and a build plugin that controls how Maven compiles that code. Both are routine changes in a Java backend project, but they serve different purposes.


A dependency supplies code to your application

Suppose the expense tracker needs to check whether a category label is empty or contains only whitespace. You could write that check yourself, or use a utility library. To use a library, declare it under <dependencies> in pom.xml; you do not need to copy its JAR into src/main/java.

Watch the dependency demonstration in Maven Tutorial - Crash Course by Marco Codes. Its example uses an MP3 library rather than an expense-tracking library, but the POM structure and selection process are the same.

Maven Tutorial - Crash Course

Watch how a library is identified and added to the POM, then how its scope affects where it is available.

Start with the declaration for the library coordinates and the distinction between the default compile scope and test scope. Then watch finding coordinates for how the presenter selects a version and refreshes the IDE after editing the POM. Focus on the XML fields rather than the particular library or version shown.

A dependency is identified by its groupId, artifactId, and version—the same kind of coordinates you used to identify your own project. For example, the Apache Commons Lang library can be declared like this:

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.11</version>
    </dependency>
</dependencies>

The outer <dependencies> element belongs directly inside <project>. Each <dependency> inside it names one library. Version 3.11 is a specific example, not a claim that it is the newest release; in a real project, check the library’s published coordinates and choose a version compatible with your project.

No <scope> appears here, so Maven uses the default, compile. That makes the library available when compiling application code and tests, and when running the application. A testing library, by contrast, normally uses <scope>test</scope> so it is available to test code without becoming an application-code dependency.

Maven looks for the specified artifact in its local cache and can download it from a configured remote repository if necessary. A dependency may bring in its own dependencies, too. For now, the important distinction is that the declaration tells Maven which library your project needs; it does not tell Maven to run a build task. Also, a dependency being available at runtime does not mean Maven automatically embeds it inside an ordinary JAR.


A plugin performs or configures build work

Maven uses plugins for build work such as compiling Java, running tests, and packaging files. A library dependency contributes classes that your application or tests use. A build plugin contributes behavior that Maven uses while building the project.

Read the short plugin example in Apache Maven’s Maven Getting Started Guide before examining the updated POM below.

Maven Getting Started Guide – Maven

Apache Maven’s guide shows where a compiler plugin goes and explains what its configuration changes.

In “How do I use plugins?”, begin at the paragraph starting “Whenever you want to customise the build for a Maven project”. Read the plugin example, including the XML between those paragraphs. Notice that the compiler plugin is already involved in a normal build: declaring it here configures that existing work. The guide’s JDK 5 settings are historical examples; do not copy them into a current project.

For an example project using Java 17, the compiler plugin declaration is:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.13.0</version>
            <configuration>
                <release>17</release>
            </configuration>
        </plugin>
    </plugins>
</build>

Here, <build><plugins> is the place for plugins used by this project’s build. The coordinates select the compiler plugin and its version; <configuration> passes a setting to that plugin. <release>17</release> tells it to compile for Java 17, so this example requires a suitable JDK 17 or newer. If your project uses a different Java release, select a value your installed JDK supports.

Declaring the compiler plugin this way configures compilation that Maven already performs; it does not add a second, separate compilation step. Other plugins may need an explicit goal or execution configured before they run as part of a build. You’ll examine Maven’s build stages and goals in the next lesson.


Put both changes into one POM

Here is the small expense-tracker POM from last lesson with both additions in place. The project’s own version, the library version, and the plugin version identify three different things.

<project>
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>expense-tracker</artifactId>
    <version>0.1.0</version>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.11</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <release>17</release>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

To see what the dependency enables, an application class at src/main/java/org/example/expenses/ExpenseLabelCheck.java could import a class from Commons Lang:

package org.example.expenses;

import org.apache.commons.lang3.StringUtils;

public class ExpenseLabelCheck {
    public static boolean isMissing(String label) {
        return StringUtils.isBlank(label);
    }
}

With a suitable JDK and Maven installed, run mvn -version to check which Java installation Maven is using, then run mvn compile from the folder containing pom.xml. Maven should resolve the library and compile this class using the configured compiler plugin. You are using compile here only as a quick check; the next lesson will explain how that command fits into Maven’s lifecycle.

If compilation says StringUtils cannot be found, check the dependency’s coordinates, version, and placement inside <dependencies>, then make sure your IDE has refreshed the Maven project. If Maven rejects the configured release, compare <release>17</release> with the JDK reported by mvn -version. These checks separate a library-resolution problem from a compiler-configuration problem.


Key takeaways

Add a library under <dependencies> when application or test code needs its classes. Add a plugin under <build><plugins> when you need to perform or configure build work. Both use coordinates, but they act on different sides of the build: your code uses the library; Maven uses the plugin.

Next, you’ll run Maven’s compile, test, and package stages and see how these POM declarations take effect during a complete build.

Can't find a good explanation? Sign up and we'll make it for you

Sign up