Maven
Maven 用生命周期、插件和依赖图组织构建。本文以 Maven 3 为范围,讲解阶段执行与依赖收敛。
1. 面试核心
先记住这几条,再读正文- 生命周期与插件。 phase 定义执行阶段,绑定的插件 goal 执行具体工作。
- 依赖仲裁。 未受显式管理时,依赖仲裁采用最近路径,同深度先声明者优先。卡 · 生命周期
- 版本管理与引入。
dependencyManagement管理版本,直接或传递依赖负责实际引入。 - 集成测试。
mvn verify会执行集成测试及其后的post-integration-test清理阶段,再由 Failsafe 检查测试结果。卡 · 集成测试阶段 - 多模块构建。
mvn -pl app -am verify构建 app 及其 reactor 依赖。卡 · 聚合、继承与 reactor
2. 生命周期
clean、default 与 site 是独立生命周期。default 的常用阶段如下:
validate → compile → test → package → verify → install → deploy
执行一个 phase 时,会先执行同生命周期的前置阶段。mvn clean verify 先清理,再执行 default 到 verify。
install 把产物安装到本地仓库。deploy 发布到远程仓库,供其他项目使用。应用上线由 CD 完成。
3. 插件 goal
phase 是阶段,goal 是插件提供的具体动作。packaging 和项目配置决定哪些 goal 绑定到阶段。
Surefire 执行单元测试,Failsafe 用于集成测试。集成测试需显式配置相应 goal。仅执行某个 phase,并不会自动补齐缺少的插件绑定。
插件版本与 JDK 版本一同固定。重复绑定需检查父 POM 与子模块的有效配置。
4. Scope
| scope | 类路径与传递 |
|---|---|
| compile | 默认范围,进入主编译、测试与运行,可传递 |
| provided | 主编译与测试可用,运行环境提供,不传递 |
| runtime | 测试与运行可用,主编译不可用 |
| test | 仅测试使用,不向下游传递 |
传递依赖的最终 scope 取决于两段路径的 scope。system 指向本地文件,降低跨环境可移植性。import 用于 dependencyManagement 中导入 POM。
5. 依赖仲裁
未显式管理的冲突按最近路径选版本。同深度时,先声明的路径优先。显式依赖与 dependencyManagement 可控制实际选择。
mvn dependency:tree
mvn help:effective-pom
依赖树显示引入路径,有效 POM 显示继承后的配置。修复可采用升级上游、精确 exclusion 或显式版本。版本统一后仍需验证二进制兼容性。
6. dependencyManagement 与 BOM
dependencyManagement 提供依赖版本与默认配置。单独列在这里的坐标不会进入类路径。直接声明或传递依赖遇到它时,管理规则才参与解析。
BOM 以 type=pom、scope=import 导入管理项。导入 BOM 本身只带入这些版本规则。
pluginManagement 提供插件版本与配置。插件由生命周期或显式声明使用后,相关配置才生效。项目的 dependencyManagement 不管理插件自身依赖。
7. 依赖收敛检查
Enforcer 的 dependencyConvergence 检查依赖路径版本一致性。它与只看仲裁后的最终 jar 集合不同。
可通过 dependencyManagement 或精确排除消除分叉。例外应写明坐标、原因和期限。重复类检查需要额外规则,并非收敛规则的职责。
8. 可重复构建
固定 JDK、Maven、依赖、插件与仓库输入。Maven Wrapper 固定 Maven 分发版本。Toolchains 声明构建所需工具链。
project.build.outputTimestamp 可稳定归档时间戳。字符集、时区和代码生成工具也需固定。
独立重建后比较产物摘要,才能验证字节级重复性。部署使用同一产物的方式见可重复交付。
9. 聚合、继承与 reactor
聚合通过 modules 收集构建模块。继承通过 parent 复用配置。聚合 POM 与父 POM 可以是同一个,也可以分开。
Reactor 根据实际依赖关系排序模块。仅列在 dependencyManagement 中的依赖不影响排序。
mvn -pl app -am verify
该命令选择 app,并构建它依赖的 reactor 模块。-amd 则选择依赖当前所选模块的下游模块。执行前需位于包含这些模块的聚合工程。
10. 集成测试阶段
配置 Failsafe 的 integration-test 与 verify goal 后,使用 mvn verify。
Failsafe 在 integration-test 阶段记录测试失败。随后 post-integration-test 阶段仍可清理测试环境。verify 阶段检查结果,并决定构建是否失败。
只运行 mvn integration-test 会跳过后续阶段。测试清理与最终结果检查因此缺失。
11. 常见问题
11.1 package 与 install
package 生成 jar 或 war。install 还将产物与 POM 写入本地仓库。Reactor 可在同次构建中使用其他模块产物。
11.2 NoSuchMethodError 与类路径
编译期目标类有该方法,运行期加载的类却缺少对应签名。检查最终包、容器依赖与启动类路径。IDE 的依赖视图只反映其中一个环境。
12 · 面试题卡片
12.1 Maven 如何选择冲突依赖,怎样让多模块版本收敛
出现公司:阿里巴巴、字节跳动、华为
考察重点
- 理解生命周期 phase 与插件 goal。
- 理解最近定义、dependency tree 和 scope。
- 理解dependencyManagement、BOM 与 convergence。
参考回答
Maven 对未受显式管理的冲突采用最近路径规则。
同深度时,先声明者优先。用 dependency:tree 确定引入路径,再决定升级或管理版本。
父 POM 或 BOM 用 dependencyManagement 统一规则。依赖仍由直接声明或传递路径引入。Enforcer 的 dependencyConvergence 检查版本分叉。CI 同时固定工具链、插件和归档时间,并执行兼容性测试。
12.2 Maven 的聚合和继承有什么区别,怎样构建一个模块及其依赖?
出现公司:暂无已核验的公开来源(本站自测)
考察重点:modules、parent 与 reactor 排序。
相关内容:聚合、继承与 reactor。
参考回答
聚合决定一起构建哪些模块,继承决定复用哪些配置。
Maven 3 中可执行 mvn -pl app -am verify。它选择 app,并构建 app 依赖的 reactor 模块。实际依赖参与排序,管理项本身不参与排序。
12.3 Failsafe 集成测试为什么应执行 verify?
出现公司:暂无已核验的公开来源(本站自测)
考察重点:测试失败、环境清理与结果检查。
相关内容:集成测试阶段。
参考回答
verify 会覆盖集成测试、后续清理和最终结果检查。
Failsafe 将测试失败留到 verify 阶段报告。post-integration-test 因此能先清理环境。仅执行 integration-test 会跳过这些后续阶段。