生命周期概述
本章是"构建生命周期"主题的起点。理解生命周期的概念、三套生命周期的分工,以及阶段(Phase)与插件目标(Goal)的关系,是后续学习 clean、default、site 各阶段细节,以及理解插件绑定机制的前提。
核心机制
Maven 基于构建生命周期的概念。这意味着构建和分发特定构件的过程被明确定义。强调:
对构建项目的人来说,只需学习一小套命令,就能构建任何 Maven 项目,而 POM 会确保他们获得期望的结果。这是 Maven"约定优于配置"在构建流程上的直接体现——你不需要告诉 Maven"先编译再测试再打包",你只需要说 mvn package,Maven 自己知道该做什么。
生命周期的概念
Maven 的生命周期是一组预定义的、有序的构建阶段(Phase)集合。每个阶段代表构建过程中的一个步骤,如编译、测试、打包。生命周期的核心设计原则是:
- 顺序性:阶段按固定顺序执行,不能跳过中间阶段
- 累积性:执行某个阶段时,该阶段之前的所有阶段都会自动执行
- 插件驱动:每个阶段本身不做事,而是绑定一个或多个插件目标(Goal)来执行具体任务
三套生命周期
Maven 有三套相互独立的生命周期,它们之间没有依赖关系,可以单独调用:
| 生命周期 | 用途 | 典型命令 |
|---|---|---|
| clean | 清理构建产物 | mvn clean |
| default | 编译、测试、打包、部署 | mvn compile、mvn test、mvn package、mvn install、mvn deploy |
| site | 生成项目文档站点 | mvn site |
关键特性:三套生命周期是独立的。执行 mvn clean 不会触发 default 生命周期的任何阶段;执行 mvn package 也不会自动清理 target/ 目录。这就是为什么常见的组合命令是 mvn clean package——显式调用两个生命周期。
阶段(Phase)的概念
阶段是生命周期中的单个步骤。每个生命周期由多个阶段按顺序组成。以 default 生命周期为例,核心阶段包括:
validate → compile → test → package → verify → install → deploy
阶段的累积性是 Maven 最重要的行为特征之一。当你执行:
mvn package
Maven 实际上会依次执行:
validate → compile → test → package
package 之前的所有阶段都会自动运行,你不需要逐个敲命令。
生活类比:汽车生产线
想象 Maven 是一套汽车组装流水线:
- 生命周期 = 整条流水线:有的流水线负责"组装新车"(default),有的负责"拆解旧车"(clean),有的负责"拍摄宣传册"(site)
- 阶段 = 流水线上的工位:组装流水线上有"焊接车架"(compile)、"安装发动机"(test)、"喷漆质检"(package)等工位
- 插件目标 = 工位上的机器人:每个工位本身只是一块空地,真正干活的是机器人(插件)。"焊接车架"工位上可能有一台焊接机器人(
maven-compiler-plugin) - 累积性 = 流水线不可跳跃:你不能直接把车开到"喷漆质检"工位,它必须先经过前面的所有工位。当你说"我要一辆成品车"(
mvn package),流水线自动从第一个工位开始,一路送到最后一个工位
这个类比的关键在于:生命周期是"流程框架",阶段是"流程节点",插件是"执行实体"。三者分离的设计,让 Maven 既能保证构建顺序的一致性,又能通过替换插件实现灵活的构建逻辑。
图示
上图展示了三套生命周期的并列关系。它们彼此独立,没有箭头相连。你可以单独调用 mvn clean,也可以单独调用 mvn package,也可以组合调用 mvn clean deploy。
上图展示了生命周期 → 阶段 → 插件目标的三层层次结构:
- 生命周期是最高层抽象,定义"要做什么类型的事"
- 阶段是中间层,定义"按什么顺序做"
- 插件目标是最底层,定义"具体怎么做"
一个阶段可以绑定多个插件目标(如 package 阶段可能同时绑定 maven-jar-plugin:jar 和 maven-source-plugin:jar-no-fork),但一个插件目标通常只绑定到一个阶段。
完整示例
场景
飞翔科技的 employee-system 项目刚完成一次功能迭代。CTO 大翔要求架构师白歌向团队演示 Maven 生命周期的基本用法,确保后端小崔、前端黄俪、运维李眉都能理解"一条命令背后发生了什么"。
操作前:对生命周期一无所知
小崔之前用 IDE 的"Build"按钮,只知道"点一下就能跑",但不清楚 Maven 在背后执行了哪些步骤。黄俪作为前端,偶尔需要启动后端服务联调,对 Maven 命令更是一头雾水。
操作步骤
白歌在项目根目录下依次执行以下命令,并解释每个命令触发的生命周期:
步骤 1:清理构建产物
mvn clean
输出:
[INFO] --- maven-clean-plugin:3.2.0:clean (default-clean) @ employee-system ---
[INFO] Deleting /home/baige/employee-system/target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
说明:触发了 clean 生命周期的 pre-clean → clean → post-clean 阶段。实际执行的是 maven-clean-plugin:clean 目标,删除了 target/ 目录。
步骤 2:编译主代码
mvn compile
输出:
[INFO] --- maven-resources-plugin:3.3.0:resources (default-resources) @ employee-system ---
[INFO] Copying 2 resources
[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ employee-system ---
[INFO] Compiling 15 source files to /home/baige/employee-system/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
说明:触发了 default 生命周期的 validate → compile 阶段。validate 阶段验证项目结构和 POM 合法性;compile 阶段调用 maven-compiler-plugin 将 src/main/java 编译到 target/classes。
步骤 3:运行测试并打包
mvn package
输出(节选):
[INFO] --- maven-resources-plugin:3.3.0:resources (default-resources) @ employee-system ---
[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ employee-system ---
[INFO] --- maven-resources-plugin:3.3.0:testResources (default-testResources) @ employee-system ---
[INFO] --- maven-compiler-plugin:3.10.1:testCompile (default-testCompile) @ employee-system ---
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ employee-system ---
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
[INFO] --- maven-jar-plugin:3.3.0:jar (default-jar) @ employee-system ---
[INFO] Building jar: /home/baige/employee-system/target/employee-system-1.0.0.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
说明:触发了 default 生命周期的 validate → compile → test → package 阶段。由于阶段的累积性,package 会自动先执行前面的所有阶段。最终生成 target/employee-system-1.0.0.jar。
操作结果
变化分析:
- 小崔理解了
mvn compile和mvn package的区别:前者只编译主代码,后者还会运行测试、打包 JAR - 黄俪学会了在联调前执行
mvn clean package,确保拿到的是"从零开始、完整构建"的产物,而非被旧文件污染的半成品 - 李眉明白了为什么 Jenkins 流水线里用
mvn clean deploy而不是mvn deploy——clean 和 default 是两个独立的生命周期,必须显式组合才能既清理又部署
易错点与常见问题
误区一:生命周期是"脚本"
错误认知:"Maven 生命周期跟 Shell 脚本一样,就是按顺序执行一堆命令。"
纠正:生命周期不是脚本,而是声明式的阶段框架。脚本里写死了"先执行 A 命令,再执行 B 命令";而 Maven 生命周期只声明了"阶段顺序",每个阶段具体做什么由绑定的插件决定。你可以在不改变生命周期的情况下,替换 compile 阶段绑定的编译器插件(如从 maven-compiler-plugin 换成 maven-scala-plugin),生命周期本身不变。这种"框架与实现分离"的设计,是 Maven 与 Ant 的本质区别。
误区二:三套生命周期是串行执行的
错误认知:"执行 mvn clean package 时,Maven 先完整执行 clean 生命周期,再完整执行 default 生命周期,所以 clean 总是在 package 之前。"
纠正:mvn clean package 中两个生命周期的执行顺序确实是 clean 先、default 后,但这不是因为生命周期之间有依赖关系,而是因为命令行中 clean 写在 package 前面。Maven 按命令行顺序调度生命周期。如果你写 mvn package clean,Maven 会先执行 default 到 package,再执行 clean——虽然这通常不是你想要的结果。三套生命周期在设计上完全独立,它们的执行顺序由命令行参数决定,而非内置依赖。
误区三:执行 mvn test 只会运行测试
错误认知:"我只想看测试跑不跑得过,所以 mvn test 应该只执行测试,不会编译。"
纠正:由于阶段的累积性,mvn test 会自动先执行 validate 和 compile。如果 src/main/java 里的代码有编译错误,mvn test 会在编译阶段就失败,根本不会进入测试阶段。如果你只想运行测试而不重新编译,需要使用插件的直接调用方式:mvn surefire:test(跳过生命周期,直接调用插件目标)。
小结
Maven 的生命周期是构建流程的"骨架",由三套相互独立的生命周期(clean、default、site)组成。每套生命周期包含多个有序的阶段,执行某个阶段时会自动累积执行其前面的所有阶段。阶段本身不执行具体任务,而是绑定插件目标来完成实际工作。理解"生命周期 → 阶段 → 插件目标"的三层结构,是掌握 Maven 构建原理的核心。
本章与全局的关系:本章描绘了生命周期的"全景地图"。接下来四章将分别深入 clean、default、site 三套生命周期的阶段细节,以及生命周期与插件的绑定机制,让你从"知道有生命周期"进化为"能精确控制每个构建步骤"。