Maven Plugins

Maven's lifecycles are hollow without plugins. A lifecycle phase is just a named stage; the actual work — compiling code, running tests, packaging JARs — is done by plugins. This chapter explains how the plugin system works and introduces the plugins you will configure most often.

Prerequisites

  • Understanding of the Maven lifecycle and phases
  • A project with a working pom.xml

How Plugins Work

Maven itself is a small core. Almost every feature you use — compilation, testing, packaging, documentation — is provided by a plugin. When you run mvn compile, Maven locates the maven-compiler-plugin, invokes its compile goal, and passes your project configuration to it.

Binding Goals to Phases

A goal is a specific task inside a plugin. Goals are bound to lifecycle phases in one of two ways:

  1. Built-in bindings — defined in Maven's Super POM for standard phases like compile, test, and package
  2. Custom bindings — declared in your pom.xml when you add a plugin

Configuring a Plugin

Plugins are declared inside the <build> section:

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>21</source>
                <target>21</target>
            </configuration>
        </plugin>
    </plugins>
</build>

The <configuration> block contains parameters specific to that plugin. Maven passes these values to the plugin when it executes.

Built-in vs. Third-Party Plugins

TypeGroup IDExample
Built-inorg.apache.maven.pluginsmaven-compiler-plugin
Third-partyVariesorg.codehaus.mojo:exec-maven-plugin

Built-in plugins are maintained by the Maven team and cover the core build tasks. Third-party plugins extend Maven for specialized needs — running applications, generating code, or integrating with cloud services.

maven-compiler-plugin

This is the plugin responsible for turning .java files into .class files. It runs during the compile and test-compile phases.

Basic Configuration

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <!-- Java language level for source files -->
                <source>21</source>
                <!-- JVM bytecode target version -->
                <target>21</target>
                <!-- Enable preview features (use with caution) -->
                <compilerArgs>
                    <arg>--enable-preview</arg>
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

Tip

Prefer Properties for Simple Cases

If you only need to set the Java version, the <maven.compiler.source> and <maven.compiler.target> properties in <properties> are enough. Use the full plugin configuration when you need advanced options like annotation processors or custom compiler arguments.

maven-surefire-plugin

This plugin runs your unit tests during the test phase. It looks for test classes matching the naming conventions *Test, Test*, and *TestCase.

Basic Configuration

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.1.2</version>
        </plugin>
    </plugins>
</build>

With JUnit 5 on the classpath, the plugin auto-detects and runs Jupiter tests without extra configuration.

Running aSubset of Tests

You can configure the plugin to include or exclude specific tests:

xml
<configuration>
    <!-- Only run tests in this package -->
    <includes>
        <include>com/example/service/**Test.java</include>
    </includes>
    <!-- Skip integration tests by name pattern -->
    <excludes>
        <exclude>**/*IntegrationTest.java</exclude>
    </excludes>
</configuration>

maven-jar-plugin

This plugin packages compiled classes into a JAR file during the package phase.

Making the JAR Executable

Remember the "no main manifest attribute" error from the First Project chapter? This plugin fixes it:

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <!-- Fully qualified main class name -->
                        <mainClass>com.example.HelloWorld</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

After adding this configuration, mvn package produces an executable JAR:

bash
# Run the JAR directly
java -jar target/minimal-project-1.0-SNAPSHOT.jar

Building a Fat JAR

A standard JAR contains only your project's classes. If your code depends on external libraries, running java -jar fails with ClassNotFoundException. A fat JAR (or uber JAR) bundles all dependencies into a single file.

maven-shade-plugin

The Shade plugin repackages dependencies into your JAR and rewrites their packages to avoid conflicts:

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.5.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <!-- Merge service provider files from dependencies -->
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

maven-assembly-plugin

The Assembly plugin offers more control over packaging formats. It can produce ZIP archives, tarballs, or JARs with custom directory layouts:

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.6.0</version>
            <configuration>
                <descriptorRefs>
                    <!-- Built-in descriptor for fat JAR -->
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>com.example.HelloWorld</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Tip

Shade vs. Assembly

Use Shade when you need a single executable JAR and want to handle dependency conflicts by relocating packages. Use Assembly when you need multiple output formats or a custom directory structure inside the archive.

exec-maven-plugin

Running your application from Maven without packaging first is convenient during development. The Exec plugin handles this.

Configuration

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <!-- Main class to execute -->
                <mainClass>com.example.HelloWorld</mainClass>
            </configuration>
        </plugin>
    </plugins>
</build>

Running the Application

bash
# Compile and run the main class
mvn exec:java

You can also override the main class on the command line:

bash
# Run a different main class temporarily
mvn exec:java -Dexec.mainClass="com.example.AnotherClass"

FAQ

How do I know which plugin version to use?

Check the official Maven plugin documentation. Each plugin page lists the latest stable version and its compatibility requirements.

Can a plugin run in multiple phases?

Yes. The <executions> block lets you bind the same plugin to different phases with different configurations. For example, you might run maven-antrun-plugin during both compile and package to perform different tasks.

What happens if two plugins are bound to the same phase?

They execute in the order they appear in the POM. If one plugin generates code and another compiles it, make sure the generator is listed first.

Why does my plugin configuration not take effect?

Double-check that the plugin version is specified. Without an explicit version, Maven falls back to the version defined in the Super POM, which may be older and ignore newer configuration parameters.

How do I list all plugins used in a build?

Run:

bash
# Display effective plugin configurations
mvn help:describe -Dcmd=compile

Or inspect the effective POM:

bash
mvn help:effective-pom | grep -A 5 "<plugins>"