Maven 核心插件详解
插件是 Maven 的执行单元。编译器、测试、打包、发布等每个构建动作都对应一个插件。本文详解最常用的六款核心插件及其关键配置。
插件速览
| 插件 | 用途 | 绑定阶段 | 关键目标 |
|---|---|---|---|
| compiler | 编译 | compile / test-compile | compile、testCompile |
| surefire | 运行单元测试 | test | test |
| jar | 打 jar 包 | package | jar |
| shade | 打可执行 fat-jar | package | shade |
| assembly | 自定义打包 | package | single |
| release | 版本发布流程 | - | prepare、perform |
maven-compiler-plugin
控制 Java 编译版本与编译参数:
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
<parameters>true</parameters> <!-- 保留方法参数名(反射用) -->
<compilerArgs>
<arg>-Xlint:unchecked</arg> <!-- 编译告警 -->
</compilerArgs>
</configuration>
</plugin>| 配置 | 说明 |
|---|---|
| source / target | 源码/字节码版本,JDK 9+ 推荐用 <release>21</release> |
| parameters | 编译保留方法参数名,Spring 反射注入依赖 |
| fork | 独立 JVM 编译(隔离内存与 classpath) |
注意:<source>/<target> 只改变字节码版本,不限制 API;要同时限制 API 应使用 <release>。
maven-surefire-plugin
运行测试(JUnit / TestNG),控制测试范围与失败策略:
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/*IT.java</exclude> <!-- 集成测试留给 failsafe -->
</excludes>
<skipTests>false</skipTests>
</configuration>
</plugin>| 用法 | 说明 |
|---|---|
mvn test | 运行单元测试(默认匹配 *Test.java、Test*.java 等) |
-DskipTests | 编译测试但不运行 |
-Dtest=OrderServiceTest | 只运行指定测试类 |
-Dtest=OrderServiceTest#create | 只运行指定方法 |
| surefire:test | 单独调用测试目标 |
集成测试(*IT.java)默认由 failsafe 插件在 integration-test 阶段运行,避免与单元测试混淆。
maven-jar-plugin
打标准 jar 包,配置 manifest(Main-Class 等):
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.App</mainClass> <!-- 可执行 jar 入口 -->
</manifest>
<manifestEntries>
<Implementation-Version>${project.version}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>普通 jar 不含依赖,直接 java -jar 会报 ClassNotFound;需要把依赖打进去时用 shade 或 assembly。
maven-shade-plugin
打 fat-jar(uber-jar),把项目与所有依赖合并到一个可执行 jar:
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.App</mainClass>
</transformer>
<!-- META-INF/services 合并(SPI) -->
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<!-- 排除签名文件(多 jar 合并会冲突) -->
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>关键配置
| 配置 | 说明 |
|---|---|
| mainClass | 可执行入口 |
| ServicesResourceTransformer | 合并 META-INF/services 下的 SPI 文件 |
| ApacheLicenseResourceTransformer | 合并 LICENSE |
| relocation | 依赖重定位(包名改写),解决与宿主应用的类冲突 |
relocation 示例
xml
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>shade.guava.com.google.common</shadedPattern>
</relocation>
</relocations>适用于 SDK/插件类 fat-jar:把 guava 重命名到 shade.guava.*,避免与使用方已有的 guava 冲突。
maven-assembly-plugin
按描述文件自定义打包(zip/tar/jar 等任意格式),灵活性最强:
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.7.1</version>
<configuration>
<descriptors>
<descriptor>src/assembly/bin.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals><goal>single</goal></goals>
</execution>
</executions>
</plugin>assembly 描述文件可定义任意目录结构:
xml
<!-- src/assembly/bin.xml -->
<assembly>
<id>bin</id>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}/bin</directory>
<outputDirectory>/bin</outputDirectory>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>/lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>shade vs assembly 选择
| 对比 | shade | assembly |
|---|---|---|
| 产物 | 单个 fat-jar | 任意结构(zip/目录) |
| 依赖合并 | 支持(含冲突重定位) | 只是收集,不合并类 |
| 场景 | 可执行 jar / SDK | 发布目录、安装包、服务包 |
maven-release-plugin
规范化版本发布流程(配合 SCM):
bash
mvn release:prepare # 检查 → 打 tag → 升级版本号 → 提交
mvn release:perform # 从 tag 构建并发布xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<tagNameFormat>v@{project.version}</tagNameFormat>
<autoVersionSubmodules>true</autoVersionSubmodules>
<pushChanges>true</pushChanges>
</configuration>
</plugin>要求:POM 配置了 <scm> 地址,且能访问 Git 仓库。release 流程会修改 POM 版本号并提交,需在有写权限的环境执行。
常见问题
- 编译后运行报 UnsupportedClassVersionError? source/target 低于运行 JDK,或改
<release>不一致。 - fat-jar 运行报 SignatureException? 多个 jar 的签名文件冲突,用 shade filters 排除
META-INF/*.SF等。 - SPI 服务不生效? fat-jar 未合并
META-INF/services,加 ServicesResourceTransformer。 - 测试被跳过却执行了?
-DskipTests与 surefireskip配置冲突,检查是否混用-Dmaven.test.skip=true。