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:
- Built-in bindings — defined in Maven's Super POM for standard phases like
compile,test, andpackage - Custom bindings — declared in your
pom.xmlwhen you add a plugin
Configuring a Plugin
Plugins are declared inside the <build> section:
<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
| Type | Group ID | Example |
|---|---|---|
| Built-in | org.apache.maven.plugins | maven-compiler-plugin |
| Third-party | Varies | org.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
<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
<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:
<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:
<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:
# Run the JAR directly
java -jar target/minimal-project-1.0-SNAPSHOT.jarBuilding 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:
<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:
<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
<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
# Compile and run the main class
mvn exec:javaYou can also override the main class on the command line:
# 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:
# Display effective plugin configurations
mvn help:describe -Dcmd=compileOr inspect the effective POM:
mvn help:effective-pom | grep -A 5 "<plugins>"