2.1 一次完整构建的旅程:三套生命周期 本节摘要:Maven 的构建过程由 clean、default、site 三套生命周期组织,default 是主战场,从 validate 到 deploy 共二十余个阶段。执行任一阶段会连带执行其全部前置阶段,而阶段本身是空壳,实际工作由绑定的插件完成。本节逐阶段走完一次完整构建,建立构建日志的全景解读能力。 一、从一条奇怪的报错说起 先看一个真实对话。新人问:"我只执行了 mvn test,为什么它把代码也编译了?测试不是只跑测试吗?" 这个疑问背后就是生命周期的核心语义:阶段不是孤立动作,而是一条传送带上的工位。你指定了终点站 test,机器就从 validate 一路站站开过来——compile 自然被路过并执行。
本节摘要:Maven 的构建过程由 clean、default、site 三套生命周期组织,default 是主战场,从 validate 到 deploy 共二十余个阶段。执行任一阶段会连带执行其全部前置阶段,而阶段本身是空壳,实际工作由绑定的插件完成。本节逐阶段走完一次完整构建,建立构建日志的全景解读能力。
先看一个真实对话。新人问:"我只执行了 mvn test,为什么它把代码也编译了?测试不是只跑测试吗?" 这个疑问背后就是生命周期的核心语义:阶段不是孤立动作,而是一条传送带上的工位。你指定了终点站 test,机器就从 validate 一路站站开过来——compile 自然被路过并执行。
这种设计的价值在于确定性:任何人以任何方式触发 test 阶段,前置条件永远满足,不存在"忘了编译就跑测试"的状态。代价是你必须记住这条隐含的顺序链,否则会出现"我只是想重新打包,为什么测试又全跑了一遍"的困惑——答案在第 1.3 节的 -DskipTests 参数里。
Maven 内置三套生命周期,各自独立、互不自动串联。

default 生命周期的完整阶段列表比图上更细(generate-resources、process-resources、generate-test-sources、process-test-sources、process-classes 等),日常高频的是图中九个。有两个阶段容易被忽视但战术价值很高:verify 挂载集成测试与质量检查(第 5 章的 SonarQube 常绑这里),process-resources 是资源过滤的执行点(2.3 节的主角)。
对 risk-engine 执行 mvn clean install,把输出按生命周期分段标注:
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ risk-engine --- [INFO] Deleting 目录 target // ↑ clean 生命周期的 clean 阶段:删除构建输出目录 [INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ risk-engine --- [INFO] Copying 3 resources // ↑ process-resources 阶段:拷贝并过滤主资源 [INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ risk-engine --- [INFO] Compiling 42 source files with javac // ↑ compile 阶段:编译主源码到 target/classes [INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ risk-engine --- [INFO] Tests run: 87, Failures: 0, Errors: 0 // ↑ test 阶段:执行单元测试 [INFO] --- maven-jar-plugin:3.2.2:jar (default-jar) @ risk-engine --- [INFO] Building jar: target/risk-engine-1.0.0.jar // ↑ package 阶段:打成 jar [INFO] --- maven-install-plugin:2.5.2:install (default-install) @ risk-engine --- [INFO] Installing jar 到本地仓库路径 // ↑ install 阶段:把成品放进本地仓库 [INFO] BUILD SUCCESS (总耗时: 12.3 s)
日志里的格式值得记住:插件:版本:goal (绑定的执行id) @ 模块。以后看到任何一行,都能立刻拆出"谁(插件)、干什么(goal)、在哪个模块"。这也解释了为什么 Maven 号称生命周期却从不自己干活——阶段只是时间表,执行者全是插件,这是下一节的主题。
补一段每个值班工程师迟早要答的题:两条生命周期的默认绑定都发生在哪些阶段。以 jar 打包为例,default 生命周期里真正有插件值守的阶段不多——资源处理两站、编译两站、surefire 的 test、jar 插件的 package、install 与 deploy 各一站,其余阶段(validate、verify 等多数)默认是空的,静默通过。这解释了一个高频疑惑:为什么 mvn verify 好像"什么都没多做"就过了?因为 verify 阶段默认没有绑定任务,它的价值在于给你一个挂载点——集成测试框架与质量检查插件(第 5 章的 SonarQube)都绑这里。空阶段不是设计缺陷,是预留的接口。
| 阶段 | jar 项目的默认值守 | 说明 |
|---|---|---|
| process-resources | resources 插件 | 拷贝并过滤主资源 2.3 节主战场 |
| compile | compiler 插件 | 主源码编译 |
| process-test-resources | resources 插件 | 测试资源处理 |
| test-compile | compiler 插件 | 测试源码编译 |
| test | surefire 插件 | 单元测试执行 |
| package | jar 插件 | 打成 jar |
| verify | 默认空 | 集成测试与质量检查的挂载点 |
| install | install 插件 | 装入本地仓库 |
| deploy | deploy 插件 | 上传远程仓库 |
顺带回答本节开头的新人问题反过来的一面:既然执行 test 必然先 compile,那"只编译不测试"怎么写?mvn compile 或 mvn test-compile(编译测试代码但不执行)。两类需求,生命周期都给了对应的站点。
mvn clean package 是手动串联两套,不是一套内的顺序;插件:版本:goal (执行id) @ 模块,每一行都能定位到生命周期位置;生命周期的时间表已经摊开,但真正在工位上拧螺丝的插件军团还没露面。下一节讲它们的编制:默认绑定怎么来、goal 怎么直接调用、以及如何往军团里塞一个你自己写的新兵。