5月31日 23:58

Maven 生命周期有哪些阶段?执行命令时到底跑了什么?

Maven 生命周期可以理解为一条已经排好顺序的构建流水线。你输入的不是“执行某个脚本”,而是告诉 Maven 要跑到哪个阶段,Maven 会把这个阶段之前的阶段一起执行。最常用的是 default 生命周期,另外还有 clean 生命周期和 site 生命周期,它们彼此独立。

clean 生命周期负责清理构建产物,典型命令是 mvn clean,会删除 target 目录。default 生命周期负责从校验、编译、测试到打包、安装、发布的主流程。site 生命周期负责生成项目站点文档,业务项目里用得少,但开源库发布文档时仍然有价值。

default 生命周期里高频阶段包括 validatecompiletestpackageverifyinstalldeploy。执行 mvn package 会先跑到测试再打包,执行 mvn install 会在打包后把产物安装到本地仓库,执行 mvn deploy 则继续上传到远程仓库。

bash
mvn clean mvn test mvn clean package mvn clean install -DskipTests mvn clean deploy -Prelease

命令执行时要看清边界

Maven 命令由生命周期阶段、插件目标、参数和 profile 共同决定。mvn clean package 里既有 clean 生命周期,也有 default 生命周期的 package 阶段;mvn dependency:tree 则是直接执行插件目标,不会自动跑完整编译流程。理解这个区别后,很多“为什么这个命令没有编译代码”的疑问就能解释清楚。

CI 里通常不会只跑 package,而会选择 verifydeployverify 更适合质量门禁,因为它给集成测试、静态检查、覆盖率校验留了位置;deploy 则适合发布流水线,前提是仓库凭证和版本策略已经配置好。取舍点是本地越快越好,CI 越确定越好,所以两边命令不必完全一样。

还有一个容易忽略的点是 profile。相同的 mvn clean package,加上 -Pprod 后可能启用不同资源、插件或依赖,最终产物也会不同。排查构建差异时,要同时记录 Maven 版本、JDK 版本、命令参数和激活的 profile,不要只说“我也是 package”。

如果项目里有集成测试,还要区分 surefire 和 failsafe。前者通常跑单元测试,后者更适合绑定到 integration-test 和 verify。这样可以把快速反馈和完整验证拆开,避免每次本地小改动都等待慢测试。本地调试时可以先用较短阶段确认问题,合并前再交给 CI 跑完整验证。

追问

mvn packageinstalldeploy 有什么区别?

package 只是在当前项目里生成 jar、war 等产物,通常放在 target 目录下。install 会把产物安装到本机 Maven 仓库,供本机其他项目依赖,所以多模块以外的本地联调经常需要它。deploy 会把产物发布到远程仓库,影响团队其他人和 CI 环境,边界更重。踩坑点是把 install 当成发布,结果别人机器上根本拿不到你的包。

为什么执行某个阶段会连前面的阶段一起跑?

生命周期的设计就是保证构建状态可靠,不能在没有编译的情况下直接测试,也不能在没测试的情况下默认打包。比如 mvn verify 会跑过 validate、compile、test、package 等前置阶段。这个规则减少了遗漏步骤,但也带来耗时问题,所以本地开发会用 -DskipTests-DskipITs 做取舍。注意跳过测试只适合本地快速验证,CI 主线不建议长期这么做。

生命周期阶段和 Maven 插件是什么关系?

阶段本身只是一个抽象节点,真正干活的是绑定到阶段上的插件 goal。比如 jar 项目的 compile 阶段通常由 maven-compiler-plugin:compile 执行,test 阶段由 maven-surefire-plugin:test 执行。你也可以直接运行插件目标,例如 mvn dependency:tree,它不一定属于完整生命周期。常见踩坑是以为改了生命周期就等于改了插件行为,实际上很多细节要在插件配置里改。

xml
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <release>17</release> </configuration> </plugin> </plugins> </build>

cleanpackage 为什么经常一起用?

clean 会删除旧的 target,可以避免历史产物、生成代码或资源文件残留影响结果。package 则负责重新编译测试并打包,两者组合能得到更干净的构建。取舍在于速度:本地频繁改代码时不一定每次都 clean,否则增量编译优势会被浪费。遇到“本地能跑、CI 不能跑”或资源文件莫名不更新时,再用 mvn clean package 排查更合适。

多模块项目执行生命周期有什么边界?

在多模块项目根目录执行命令时,Maven reactor 会按模块依赖顺序跑同一个阶段。用 -pl 可以限制模块范围,用 -am 可以补上被依赖模块,这对大仓库节省时间很有用。边界是 Maven 只理解 POM 里的模块和依赖关系,不会自动知道你运行时通过反射、脚本或配置文件间接依赖了哪个模块。遇到这种隐式依赖,最好把模块依赖显式化,否则局部构建很容易漏东西。

标签:Maven