Master Maven Plugins: From Basics to Custom Plugin Development
This guide explains Maven's core plugin architecture, demonstrates practical configurations for flatten‑maven‑plugin and exec‑maven‑plugin, details the MOJO concept behind plugins, and walks through creating, installing, and binding a custom Maven plugin to the build lifecycle.
Maven and Its Plugins
Maven is a framework that orchestrates a collection of plugins. Each build operation is delegated to a plugin goal, and the mvn command simply invokes the appropriate plugin.
<project>
<!-- other configuration -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>Even without explicit declarations, Maven provides standard plugins that support basic goals such as clean and compile with default configurations.
Practical Plugin Examples
Flatten Maven Plugin
This plugin flattens a multi‑module project's dependencies by resolving version placeholders and writing a single consolidated POM.
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.2.5</version>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
<updatePomFile>true</updatePomFile>
<ignoreTransitiveDependencies>true</ignoreTransitiveDependencies>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>generate-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>outputDirectory : Directory where the flattened POM is written.
flattenMode : resolveCiFriendliesOnly replaces placeholders such as ${revision}, ${sha1}, ${changelist} when invoked, e.g., mvn -Drevision=2.0.0-SNAPSHOT clean package.
updatePomFile : Updates the generated flattened POM.
ignoreTransitiveDependencies : Excludes transitive dependencies from the flattened POM.
Running mvn generate-resources creates a .flattened-pom.xml file in the specified directory; mvn clean removes it.
Exec Maven Plugin
This plugin runs external programs or scripts during the build. A typical use case is invoking the Allatori Java obfuscation tool.
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>obfuscate</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>java</executable>
<arguments>
<argument>-jar</argument>
<argument>path/to/allatori.jar</argument>
<argument>-config</argument>
<argument>path/to/allatori-config.xml</argument>
<argument>-in</argument>
<argument>${project.build.directory}/${project.build.finalName}.jar</argument>
<argument>-out</argument>
<argument>${project.build.directory}/${project.build.finalName}-obfuscated.jar</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build> <executable>java</executable>: Executes the Java command.
Arguments specify the Allatori JAR, its configuration file, the input JAR to be obfuscated, and the output JAR path.
During the package phase the plugin runs a command equivalent to:
java -jar path/to/allatori.jar -config path/to/allatori-config.xml -in ${project.build.directory}/${project.build.finalName}.jar -out ${project.build.directory}/${project.build.finalName}-obfuscated.jarAllatori configuration details are documented at https://allatori.com/doc.html.
Maven Plugin Mechanics (MOJO)
Each Maven plugin is built around a MOJO (Maven Plain Old Java Object). A MOJO is a regular Java class—usually extending org.apache.maven.plugin.AbstractMojo —that implements a specific build goal.
Java class : Contains the execution logic.
Goal : One or more goals defined by the MOJO, bindable to lifecycle phases.
Annotations : @Mojo marks the class as a plugin entry point; @Parameter maps configuration fields to POM elements or command‑line properties.
Configuration parameters are supplied via pom.xml or command‑line properties, allowing fine‑grained control over the MOJO's behavior.
@Mojo(name = "query")
public class MyQueryMojo extends AbstractMojo {
@Parameter(property = "query.url", required = true)
private String url;
@Parameter(property = "timeout", defaultValue = "50")
private int timeout;
@Parameter(property = "options")
private String[] options;
@Override
public void execute() throws MojoExecutionException {
// custom logic here
}
}To use the plugin, declare it in the project's pom.xml and map configuration elements to the MOJO fields:
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-myquery-plugin</artifactId>
<version>1.0</version>
<configuration>
<url>http://www.foobar.com/query</url>
<timeout>10</timeout>
<options>
<option>one</option>
<option>two</option>
<option>three</option>
</options>
</configuration>
</plugin>
</plugins>
</build>
...
</project>Creating a Custom Maven Plugin
Project Setup
Start a new Maven project with packaging set to maven-plugin and add the required dependencies and plugin‑management entries.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>sample.plugin</groupId>
<artifactId>hello-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<properties>
<maven-plugin-tools.version>3.15.1</maven-plugin-tools.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.9.9</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>${maven-plugin-tools.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>${maven-plugin-tools.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>Implementing the Mojo
Create a class that extends AbstractMojo and annotate it with @Mojo. The example below prints "Hello, world." during the build.
package org.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
/**
* Says "Hi" to the user.
*/
@Mojo(name = "sayhi")
public class GreetingMojo extends AbstractMojo {
@Override
public void execute() throws MojoExecutionException {
getLog().info("Hello, world.");
}
}Build and install the plugin to the local repository:
mvn installUsing the Custom Plugin
In any other Maven project, add the plugin to the <plugins> section and invoke its goal directly:
mvn sample.plugin:hello-maven-plugin:1.0-SNAPSHOT:sayhiYou can also bind the goal to a lifecycle phase (e.g., clean) so that it runs automatically before that phase.
Binding a Plugin to a Lifecycle Phase
Configure the plugin execution with a specific <phase> (e.g., clean) so Maven runs it automatically during that phase.
<plugin>
<groupId>sample.plugin</groupId>
<artifactId>hello-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<id>sayhi-before-clean</id>
<phase>clean</phase>
<goals>
<goal>sayhi</goal>
</goals>
</execution>
</executions>
</plugin>Running mvn clean now triggers the custom sayhi goal automatically before the clean operation.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
