适用读者:会
mvn package但说不清"多模块怎么排、依赖从哪来"的人。本系列第一篇。
前置:S01-B03(Maven 安装与镜像)、S01-J07(依赖治理)。
实测环境:Maven 3.8.7 / JDK 17(2026-11-05)。
覆盖旧文:A-1855(Maven 捋清楚,242 点)。
覆盖扩展:E-S06-001(三层模型)、E-S06-002(skipTests 两种开关)。
下一篇:S06-B02 · Lombok。
1. 一句话结论
Maven 只有三个概念:坐标(我是谁)、生命周期(怎么造)、仓库(零件从哪来)。多模块只是"把一个坐标拆成多个,用 parent 串起来"。
| 层 | 内容 | 出问题时的表现 |
|---|---|---|
| 坐标 | groupId:artifactId:version |
依赖找不到(坐标错/版本不存在) |
| 生命周期 | validate→compile→test→package→verify→install→deploy | 跑错阶段(以为只编译,其实跑了测试) |
| 仓库 | 本地 → 镜像/私服 → 中央 | 拉不到(网络/镜像/权限) |
与 S01 的分工:S01-B03 讲"装与镜像"、S01-J07 讲"依赖冲突",本篇只讲工程结构。
2. 实测:三模块工程的构建顺序
ms-parent (pom)
├── core (jar)
└── app (jar,依赖 core)
$ mvn -B -pl app -am validate
实测输出:
[INFO] Reactor Build Order:
[INFO] ms-parent [pom]
[INFO] core [jar]
[INFO] app [jar]
| 参数 | 作用 |
|---|---|
-pl app |
只构建 app 模块 |
-am(also make) |
同时构建它依赖的模块 |
-amd |
构建依赖它的模块(下游) |
-rf :core |
从 core 继续(失败续跑) |
"reactor"就是 Maven 自己算出来的构建顺序——它按依赖关系拓扑排序,parent 永远最前。
3. 三种"版本放哪"的方式
| 方式 | 写法 | 适用 |
|---|---|---|
parent 继承 |
<parent> + 子模块省略版本 |
单一项目族 |
dependencyManagement(BOM) |
只声明版本,不引入依赖 | 多项目共享版本 |
import 外部 BOM |
<scope>import</scope> |
用 Spring Boot 等官方 BOM |
判据:"这个版本是"我们项目的",还是"全公司/全生态的"?" 后者 → BOM。
4. 实测:-DskipTests 与 -Dmaven.test.skip=true 的区别(E-S06-002)
场景:core 里有一个用了 JUnit 的测试类,但没有引入 JUnit 依赖。
$ mvn -pl app -am package -DskipTests
[ERROR] COMPILATION ERROR :
[ERROR] CalcTest.java:[2,29] package org.junit.jupiter.api does not exist
失败。因为 -DskipTests 只跳过"运行测试",仍然编译测试源码。
$ mvn -pl app -am package -Dmaven.test.skip=true
# BUILD SUCCESS → app/target/app-1.0.0.jar, core/target/core-1.0.0.jar
| 开关 | 编译测试 | 运行测试 | 适用 |
|---|---|---|---|
-DskipTests |
✅ 仍编译 | ❌ 跳过 | 测试代码可编译、只是不想跑 |
-Dmaven.test.skip=true |
❌ 不编译 | ❌ 跳过 | 缺测试依赖、快速打包 |
-Dtest=Xxx |
✅ | 只跑指定测试 | 定位单个失败 |
这条差异解释了大量"本地能打包、CI 挂了"的困惑:CI 上用了 -DskipTests,而测试依赖没配齐。
5. 一个最小可跑的多模块工程(实测)
$ java -cp app/target/classes:core/target/classes demo.Main
1+2=3
注意:手工 -cp 只是演示;生产用 maven-jar-plugin + manifest 或 spring-boot-maven-plugin 打可执行 jar。
6. 五个误区
| 误区 | 真相 |
|---|---|
"-DskipTests 不编译测试" |
仍编译(E-S06-002 实测) |
"mvn package 只打包" |
会跑 compile+test |
| "子模块必须写 version" | 有 parent/BOM 时可省 |
"-pl 会带上依赖模块" |
需 -am |
| "多模块=微服务" | 只是构建单元,与部署无关 |
7. 自检
- Maven 三层模型分别对应什么故障现象?
-pl与-am的区别?- reactor 顺序由什么决定?
-DskipTests与-Dmaven.test.skip=true的差别(用实测解释)?- 什么时候该用 BOM 而不是 parent?
8. 下一篇
S06-B02 · Lombok:注解矩阵、原理与陷阱
构建讲完,下一篇讲"编译期变出代码"的机制:Lombok 的 16 个注解、它为什么需要 IDE 插件,以及三个常见陷阱。
本篇属于系列《S06 · Java 工程化与组件》。旧文来源:A-1855;扩展点 E-S06-001/002。多模块与两种 skip 开关为 2026-11-05 实测(Maven 3.8.7 / JDK 17)。