Useful Maven Plugins for Every Java Projects

Yangli
5 min readJul 18, 2024

--

The following Maven plugins may help you to create a consistent dev environment and CICD pipeline across teams.

  • Maven compiler Plugin
  • Lombok Maven Plugin
  • Maven Resources Plugin
  • Maven Enforcer Plugin
  • Maven Assembly Plugin
  • Git Commit Id Maven Plugin
  • Spring Boot Maven Plugin
  • Maven Surefire Plugin
  • Maven Failsafe Plugin

Gradle is more powerful and flexible, provides more features. But it is a little difficult to understand and may be too flexible if not well-controlled (imperative vs. declarative).

If mostly you work with small Microservices modules, Maven features may cover all you need out-of-box.

Common Properties

  <properties>
<maven.build.timestamp.format>yyyy-MM-dd_HH-mm-ss</maven.build.timestamp.format>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.release.version>21</java.release.version>

<java.compatible.versions>[21, 23)</java.compatible.versions>
<maven.compatible.versions>[3.6.3, 4.0.0)</maven.compatible.versions>
<maven.compiler.source>${java.release.version}</maven.compiler.source>
<maven.compiler.target>${java.release.version}</maven.compiler.target>

<git-commit-id-maven-plugin.skip>false</git-commit-id-maven-plugin.skip>
<spotless-maven-plugin.skip>false</spotless-maven-plugin.skip>
</properties>

Maven Compiler Plugin with Lombok

      <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>

Lombok Maven Plugin

If need to transfer Lombok annotations to regular Java code.

      <plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>${lombok-maven-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>delombok</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>

Maven Resource Plugin

To create a Uber Jar

      <plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<escapeString>\</escapeString>
</configuration>
<executions>
<execution>
<id>copy-jar</id>
<goals>
<goal>copy-resources</goal>
</goals>
<phase>package</phase>
<configuration>
<outputDirectory>${your.build.directory}/appBundle</outputDirectory>
<resources>
<resource>
<filtering>false</filtering>
<directory>${your.build.directory}</directory>
<includes>
<include>*.jar</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-config</id>
<goals>
<goal>copy-resources</goal>
</goals>
<phase>package</phase>
<configuration>
<outputDirectory>${your.build.directory}/cloud/.openshift</outputDirectory>
<resources>
<resource>
<directory>.openshift</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>

To create a library as dependency

          <plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<goals>
<goal>flatten</goal>
</goals>
<phase>process-resources</phase>
</execution>
<execution>
<id>flatten.clean</id>
<goals>
<goal>clean</goal>
</goals>
<phase>clean</phase>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
</configuration>
<executions>
<execution>
<id>copy-jar</id>
<phase>none</phase>
</execution>
<execution>
<id>copy-config</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>spring-boot</id>
<phase>none</phase>
</execution>
</executions>
</plugin>

Maven Enforcer Plugin

Enforce dev environment across teams.

      <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<configuration>
<rules>
<banDuplicatePomDependencyVersions></banDuplicatePomDependencyVersions>
<requireJavaVersion>
<version>${java.compatible.versions}</version>
</requireJavaVersion>
<requireMavenVersion>
<version>${maven.compatible.versions}</version>
</requireMavenVersion>
<requireProperty>
<property>spring-boot.version</property>
<message>spring-boot.version should be defined!</message>
<regex>.*\d.*</regex>
<regexMessage>The spring-boot.version must contain at least one digit.</regexMessage>
</requireProperty>
<bannedPlugins>
<!-- will only display a warning but does not fail the build. -->
<level>WARN</level>
<excludes>
<exclude>org.apache.maven.plugins:maven-verifier-plugin</exclude>
</excludes>
<message>Please consider using the maven-invoker-plugin (http://maven.apache.org/plugins/maven-invoker-plugin/)!</message>
</bannedPlugins>
<banDynamicVersions>
<ignores>
<ignore>package.to.be.ignored</ignore>
</ignores>
<allowSnapshots>${flag.allowSnapshots}</allowSnapshots>
<excludedScopes>test</excludedScopes>
</banDynamicVersions>
<requireFilesExist>
<files>
<file>${project.basedir}/.mvn/jvm.config</file>
<file>${project.basedir}/lombok.config</file>
</files>
</requireFilesExist>
</rules>
</configuration>
<executions>
<execution>
<id>enforce-rules</id>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>

lombok.config

config.stopBubbling = true

# Copy the Qualifier annotation from the instance variables to the constructor
# see https://github.com/rzwitserloot/lombok/issues/745
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Value

Lombok's access to jdk.compiler's internal packages
jvm.config

--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED

Maven Assembly Plugin

      <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<tarLongFileMode>posix</tarLongFileMode>
<descriptors>
<descriptor>${basedir}/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>

assembly.xml

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 http://maven.apache.org/xsd/assembly-2.2.0.xsd">
<id>bin</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.directory}/appBundle</directory>
<outputDirectory>.</outputDirectory>
</fileSet>
</fileSets>
</assembly>

Git Commit Id Maven Plugin

Create git info to be used in Spring Boot Actuator info page.

      <plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<configuration>
<skip>${git-commit-id-maven-plugin.skip}</skip>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
</configuration>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
<phase>validate</phase>
</execution>
</executions>
</plugin>

Spring Boot Maven Plugin

      <plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${SpringBoot-Start-Class}</mainClass>
<excludeDevtools>false</excludeDevtools>
</configuration>
<executions>
<execution>
<id>spring-boot</id>
<goals>
<goal>build-info</goal>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>

Spotless Maven Plugin

Same set of code formats across teams.

     <plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>${spotless-maven-plugin.version}</version>
<configuration>
<!-- optional: limit format enforcement to just the files changed by this feature branch
<ratchetFrom>origin/main</ratchetFrom> -->
<skip>${spotless-maven-plugin.skip}</skip>
<formats>
<!-- you can define as many formats as you want, each is independent -->
<format>
<!-- define the files to apply to -->
<includes>
<include>.gitignore</include>
<include>jvm.config</include>
<include>**/*.md</include>
<include>**/*.xml</include>
<include>**/*.yaml</include>
<include>**/*.yml</include>
</includes>
<excludes>
<exclude>pom.xml</exclude>
<exclude>.flattened-pom.xml</exclude>
<exclude>**/target/**/*</exclude>
<exclude>.git/**</exclude>
<exclude>setup/**</exclude>
</excludes>

<!-- define the steps to apply to those files -->
<trimTrailingWhitespace></trimTrailingWhitespace>
<endWithNewline></endWithNewline>
<indent>
<spaces>true</spaces>
<spacesPerTab>4</spacesPerTab>
</indent>

<replace>
<!-- specify replacements using search and replace -->
<name>Say Hello to Mars</name>
<search>Mars</search>
<replacement>Mars</replacement>
</replace>

<replaceRegex>
<!-- specify replacements using regex match and replace -->
<name>Say Hello to Mars from Regex</name>
<searchRegex>(Hello) W[a-z]{3}d</searchRegex>
<replacement>$1 Mars</replacement>
</replaceRegex>
</format>
</formats>
<!-- define a language-specific format -->
<pom>
<!-- These are the defaults, you can override if you want -->
<includes>
<include>pom.xml</include>
</includes>
<sortPom>
<encoding>UTF-8</encoding>
<!-- The encoding of the pom files -->
<lineSeparator>${line.separator}</lineSeparator>
<!-- line separator to use -->
<expandEmptyElements>true</expandEmptyElements>
<!-- Should empty elements be expanded-->
<spaceBeforeCloseEmptyElement>false</spaceBeforeCloseEmptyElement>
<!-- Should a space be added inside self-closing elements-->
<keepBlankLines>true</keepBlankLines>
<!-- Keep empty lines -->
<nrOfIndentSpace>2</nrOfIndentSpace>
<!-- Indentation -->
<indentBlankLines>false</indentBlankLines>
<!-- Should empty lines be indented -->
<indentSchemaLocation>false</indentSchemaLocation>
<!-- Should schema locations be indended -->
<predefinedSortOrder>recommended_2008_06</predefinedSortOrder>
<!-- Sort order of elements: https://github.com/Ekryd/sortpom/wiki/PredefinedSortOrderProfiles-->
<sortOrderFile></sortOrderFile>
<!-- Custom sort order of elements: https://raw.githubusercontent.com/Ekryd/sortpom/master/sorter/src/main/resources/custom_1.xml -->
<sortDependencies></sortDependencies>
<!-- Sort dependencies: https://github.com/Ekryd/sortpom/wiki/SortDependencies-->
<sortDependencyExclusions></sortDependencyExclusions>
<!-- Sort dependency exclusions: https://github.com/Ekryd/sortpom/wiki/SortDependencies-->
<sortPlugins></sortPlugins>
<!-- Sort plugins: https://github.com/Ekryd/sortpom/wiki/SortPlugins -->
<sortProperties>false</sortProperties>
<!-- Sort properties -->
<sortModules>false</sortModules>
<!-- Sort modules -->
<sortExecutions>false</sortExecutions>
<!-- Sort plugin executions -->
</sortPom>
</pom>

<java>
<!-- These are the defaults, you can override if you want -->
<includes>
<include>src/main/java/**/*.java</include>
<include>src/test/java/**/*.java</include>
</includes>

<!-- apply a specific flavor of google-java-format and reflow long strings -->
<googleJavaFormat>
<version>${google-java-format.version}</version>
<style>AOSP</style>
<reflowLongStrings>true</reflowLongStrings>
</googleJavaFormat>

<importOrder>
<!-- or a custom ordering -->
<wildcardsLast>true</wildcardsLast>
<!-- Optional, default false. Sort wildcard import after specific imports -->
<order>java|javax,org,com,,com.abcdef,\#,\#com.abcdef</order>
<!-- or use <file>${project.basedir}/eclipse.importorder</file> -->
<!-- you can use an empty string for all the imports you didn't specify explicitly,
'|' to join group without blank line, and '\#` prefix for static imports. -->
</importOrder>

<removeUnusedImports></removeUnusedImports>
<!-- self-explanatory -->

<formatAnnotations></formatAnnotations>
<!-- fixes formatting of type annotations, see below -->

<!-- make sure every file has the following copyright header.
optionally, Spotless can set copyright years by digging
through git history (see "license" section below) -->
<licenseHeader>
<content>/* (C)$YEAR */</content>
<!-- or <file>${project.basedir}/license-header</file> -->
</licenseHeader>
</java>

<json>
<includes>
<!-- You have to set the target manually -->
<include>src/**/*.json</include>
</includes>

<trimTrailingWhitespace></trimTrailingWhitespace>
<endWithNewline></endWithNewline>
<indent>
<spaces>true</spaces>
<spacesPerTab>4</spacesPerTab>
</indent>
<jackson>
<features>
<!-- optional: Customize the set of features (based on com.fasterxml.jackson.databind.SerializationFeature) -->
<INDENT_OUTPUT>true</INDENT_OUTPUT>
<!-- true by default -->
<ORDER_MAP_ENTRIES_BY_KEYS>false</ORDER_MAP_ENTRIES_BY_KEYS>
<!-- false by default -->
</features>
</jackson>
</json>

<sql>
<!-- You have to set the target manually -->
<includes>
<include>src/main/**/*.sql</include>
</includes>

<trimTrailingWhitespace></trimTrailingWhitespace>
<endWithNewline></endWithNewline>
<indent>
<spaces>true</spaces>
<spacesPerTab>4</spacesPerTab>
</indent>

<dbeaver>
<!-- optional
<configFile>dbeaver.properties</configFile>
-->
</dbeaver>
</sql>

</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>

Maven Surefire Plugin/Maven Failsafe Plugin

Unit tests and Integration Tests

      <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>false</skipTests>
<testFailureIgnore>true</testFailureIgnore>
<forkCount>0</forkCount>
<argLine>-Xmx1024m -XX:MaxPermSize=256m</argLine>
<excludes>
<exclude>*Validation.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<!-- Depends on which server you may use, such as Jetty or Tomcat,
configure this plugin by following this:
https://maven.apache.org/surefire/maven-failsafe-plugin/usage.html
-->
</plugin>

--

--