Maven Build 配置实践
核心理解:Maven 的
<build>配置本质上就是告诉 Maven —— "用什么插件、在什么时候、做什么事"。本文将围绕pluginManagement和plugins的组合,总结出几种典型配置模式。
一、相关概念
1.1 官方定义
"Build plugins will be executed during the build and they should be configured in the
<build/>element from the POM."—— Maven 官方文档
1.2 pluginManagement vs plugins 区别
pluginManagement | plugins | |
|---|---|---|
| 作用 | 定义插件的版本和默认配置 | 实际执行插件 |
| 是否执行 | 不执行,仅声明 | 在当前模块执行 |
| 继承性 | 子模块可继承,但需显式声明 | 子模块默认继承 |
| 典型用途 | 父 POM 统一管理插件版本 | 当前模块真正使用的插件 |
1.3 插件的两种配置位置
| 配置位置 | 适用场景 | 示例 |
|---|---|---|
<pluginManagement> | 父 POM 中统一定义版本和配置 | compiler、surefire 等几乎所有插件 |
<plugins> | 直接执行插件 | flatten、spring-boot、docker |
二、五种 Build 配置模式
模式一:父 POM 定义 + 子模块继承(最常用)
适用场景:多模块项目,所有子模块共享相同的插件配置。
父 POM 配置:
<build>
<pluginManagement>
<plugins>
<!-- 定义版本和配置,供所有子模块继承 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
子模块配置:
<build>
<plugins>
<!-- 只需引用,不用写 version 和 configuration -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
</build>
流程图:
父 POM
│
└── pluginManagement 定义 compiler 插件(版本 3.11.0,参数 -parameters)
│
▼
子模块 A 子模块 B
│ │
└── 引用 compiler 插件 └── 引用 compiler 插件
版本和配置自动继承 版本和配置自动继承
模式二:父 POM 直接执行(共享插件)
适用场景:所有模块(包括父 POM 自身)都需要执行的插件。
父 POM 配置:
<build>
<plugins>
<!-- 版本管理插件:父 POM 和所有子模块都需要 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<configuration>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
<updatePomFile>true</updatePomFile>
</configuration>
<executions>
<execution>
<goals>
<goal>flatten</goal>
</goals>
<phase>process-resources</phase>
</execution>
<execution>
<goals>
<goal>clean</goal>
</goals>
<phase>clean</phase>
</execution>
</executions>
</plugin>
<!-- 代码格式化插件:统一代码风格 -->
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.43.0</version>
<configuration>
<java>
<googleJavaFormat/>
</java>
</configuration>
</plugin>
</plugins>
</build>
子模块:自动继承,无需任何配置。
父 POM
│
└── plugins 配置 flatten、spotless
│
▼
子模块 A 子模块 B
│ │
└── 自动继承 flatten └── 自动继承 flatten
自动继承 spotless 自动继承 spotless
模式三:模块特有插件
适用场景:某些模块有特殊需求,需要额外插件(如 API 模块 vs Biz 模块)。
API 模块(只定义接口,不需要 Spring Boot 打包):
<build>
<plugins>
<!-- 仅需编译插件,继承自父 POM -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
BIZ 模块(需要 Spring Boot 打包 + Docker 镜像):
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 继承自父 POM -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<!-- 模块特有:Spring Boot 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 模块特有:Docker 镜像构建 -->
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.43.4</version>
<configuration>
<images>
<image>
<name>${docker.registry}/${project.artifactId}:${project.version}</name>
<build>
<contextDir>${project.basedir}</contextDir>
</build>
</image>
</images>
</configuration>
</plugin>
</plugins>
</build>
模块对比:
| 模块 | 编译插件 | Spring Boot 打包 | Docker 构建 |
|---|---|---|---|
-api | 继承 | 不需要 | 不需要 |
-biz | 继承 | 特有 | 特有 |
模式四:父子分离(定义与执行分离)—— 标准企业级实践
适用场景:大团队多模块项目,父 POM 统一定义,子模块按需引用或覆盖。
父 POM(只定义,不执行):
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.43.4</version>
</plugin>
</plugins>
</pluginManagement>
<!-- 父 POM 自身需要的插件 -->
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<!-- 配置和执行 -->
</plugin>
</plugins>
</build>
子模块(按需引用,可覆盖配置):
<!-- API 模块:只需要编译 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- version 和 configuration 继承父 POM -->
</plugin>
</plugins>
</build>
<!-- BIZ 模块:需要编译 + Spring Boot 打包 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- BIZ 模块如需覆盖父 POM 配置 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- 重新配置,覆盖父 POM -->
<configuration>
<source>21</source> <!-- 子模块使用更高版本 -->
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
模式五:内联配置(单体项目)
适用场景:简单单体项目,无需多模块继承。
<build>
<finalName>myapp</finalName>
<plugins>
<!-- 所有插件直接配置完整信息 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.43.4</version>
<configuration>
<images>
<image>
<name>myapp:latest</name>
<build>
<contextDir>${project.basedir}</contextDir>
</build>
</image>
</images>
</configuration>
</plugin>
</plugins>
</build>
三、模式选择决策树

四、最佳实践总结
4.1 配置原则
| 原则 | 说明 |
|---|---|
| 版本集中管理 | 所有插件版本在父 POM pluginManagement 中定义 |
| 按需引用 | 子模块只引用需要的插件,不写版本号 |
| 配置继承 | 通用配置在父 POM 中定义,特殊配置在子模块覆盖 |
| 职责分离 | 父 POM 负责"定义",子模块负责"使用" |
| 模块对齐 | API 模块只编译,BIZ 模块才打包和构建镜像 |
4.2 推荐的企业级配置结构
根父 POM
├── pluginManagement(定义所有插件版本和通用配置)
│ ├── maven-compiler-plugin → 编译(含 Lombok + MapStruct)
│ ├── maven-surefire-plugin → 测试(JUnit 5)
│ ├── maven-jar-plugin → 打包
│ ├── spring-boot-maven-plugin → Spring Boot 打包
│ ├── docker-maven-plugin → Docker 构建
│ └── ... 其他插件
│
└── plugins(父 POM 自身需要)
└── flatten-maven-plugin → 统一版本管理
子模块 API
└── plugins
└── maven-compiler-plugin → 仅编译(继承父 POM 配置)
子模块 BIZ
└── plugins
├── maven-compiler-plugin → 编译(继承)
├── spring-boot-maven-plugin → 可执行 JAR(特有)
└── docker-maven-plugin → Docker 镜像(特有)
4.3 不同模块类型的配置要求
| 模块类型 | 打包类型 | 需要的插件 |
|---|---|---|
| 根父 POM | pom | flatten、spotless |
| 聚合模块 | pom | 无(继承父 POM 即可) |
| API 模块 | jar | compiler(继承)、source(可选) |
| BIZ 模块 | jar | compiler(继承)+ spring-boot + docker |
五、快速参考
5.1 何时用 pluginManagement
- 父 POM 中定义插件版本和配置
- 让子模块使用统一版本的插件
- 集中管理所有插件的配置
5.2 何时用 plugins
- 父 POM 自身需要执行的插件(如
flatten) - 子模块特有的插件(如
spring-boot、docker) - 需要绑定到生命周期执行的插件
5.3 常见错误
| 错误 | 后果 | 正确做法 |
|---|---|---|
只在 pluginManagement 中定义,子模块不引用 | 插件不执行 | 子模块 <plugins> 中引用 |
| 子模块中写版本号 | 版本管理分散 | 省略版本,从父 POM 继承 |
在 plugins 中定义但不想让子模块继承 | 子模块被迫执行 | 在父 POM 的 pluginManagement 中定义 |
子模块覆盖父 POM 配置时写 <version> | 可能版本不一致 | 省略版本,只写 <configuration> 覆盖 |