Maven 生命周期与插件绑定
一、核心概念
Maven 有三个核心概念,很多人容易混淆:
| 概念 | 比喻 | 说明 |
|---|---|---|
| 生命周期(Lifecycle) | 菜谱的"步骤清单" | 定义构建过程有哪些阶段(phase),但不关心具体怎么做 |
| 阶段(Phase) | 菜谱中的"第3步:热锅" | 生命周期中的一个节点,有固定顺序 |
| 插件目标(Goal) | 具体的"烹饪动作" | 真正干活的单元,如 javac 编译、jar 打包 |
| 绑定(Binding) | 把"第3步"绑定到"热锅"这个动作 | 让某个阶段执行时,自动调用某个插件的目标 |
总结:生命周期定义"什么时候做",插件定义"做什么",绑定决定"做哪个"。
二、三大生命周期(Lifecycle)
Maven 内置了 3 个独立的生命周期:
| 生命周期 | 作用 | 典型阶段 |
|---|---|---|
| clean | 清理项目 | pre-clean → clean → post-clean |
| default | 构建项目(核心) | validate → compile → test → package → install → deploy |
| site | 生成项目报告/站点 | pre-site → site → post-site → site-deploy |
关键理解
- 三大生命周期相互独立,执行
mvn clean install是先跑 clean 生命周期,再跑 default 生命周期。 - 但 default 内部的 phase 是顺序执行的:执行
mvn package会自动先执行validate、compile、test,最后才执行package。
三、default 生命周期完整阶段(22 个)
| 阶段(Phase) | 作用 | 默认绑定的插件目标 |
|---|---|---|
validate | 验证项目结构是否正确 | 无(通常用于检查 POM) |
initialize | 初始化构建状态 | 无 |
generate-sources | 生成源码(如 JAXB 生成 Java 类) | 视插件而定 |
process-sources | 处理源码(如过滤资源中的占位符) | maven-resources-plugin:resources(处理主资源) |
generate-resources | 生成资源文件 | 无 |
process-resources | 复制并处理资源文件到 target/classes | maven-resources-plugin:resources |
compile | 编译主代码 | maven-compiler-plugin:compile |
process-classes | 处理编译后的 class 文件(如字节码增强) | 无 |
generate-test-sources | 生成测试源码 | 无 |
process-test-sources | 处理测试源码 | 无 |
generate-test-resources | 生成测试资源文件 | 无 |
process-test-resources | 复制并处理测试资源 | maven-resources-plugin:testResources |
test-compile | 编译测试代码 | maven-compiler-plugin:testCompile |
process-test-classes | 处理测试 class 文件 | 无 |
test | 运行单元测试 | maven-surefire-plugin:test |
prepare-package | 打包前的准备工作 | 无 |
package | 打包(jar/war/ear) | maven-jar-plugin:jar(或 war/ear 插件) |
pre-integration-test | 集成测试前准备 | 无 |
integration-test | 执行集成测试 | 无 |
post-integration-test | 集成测试后清理 | 无 |
verify | 验证包是否符合质量标准 | maven-failsafe-plugin:verify(集成测试验证) |
install | 安装到本地仓库 | maven-install-plugin:install |
deploy | 部署到远程仓库 | maven-deploy-plugin:deploy |
四、插件默认绑定关系
以下插件有默认绑定,无需在 POM 中显式配置即可工作:
| 插件 | 默认绑定的 Phase | 默认目标(Goal) | 触发条件 |
|---|---|---|---|
maven-resources-plugin | process-resources | resources | 有 src/main/resources |
maven-resources-plugin | process-test-resources | testResources | 有 src/test/resources |
maven-compiler-plugin | compile | compile | 有 src/main/java |
maven-compiler-plugin | test-compile | testCompile | 有 src/test/java |
maven-surefire-plugin | test | test | 有测试类 |
maven-jar-plugin | package | jar | packaging = jar |
maven-war-plugin | package | war | packaging = war |
maven-ear-plugin | package | ear | packaging = ear |
maven-install-plugin | install | install | 始终绑定 |
maven-deploy-plugin | deploy | deploy | 始终绑定 |
五、自定义插件绑定(<executions>)
如果想在某个阶段执行自定义插件,使用 <executions> 配置:
示例 1:在 compile 阶段自动生成代码
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>generate-sources</id>
<!-- 自定义绑定到 generate-sources 阶段 -->
<phase>generate-sources</phase>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>src/main/xsd</sources>
</configuration>
</plugin>
</plugins>
</build>
示例 2:package 阶段复制依赖到 lib 目录
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
示例 3:绑定多个 goal 到不同阶段
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<!-- 绑定到 clean 阶段 -->
<execution>
<id>clean-something</id>
<phase>clean</phase>
<goals><goal>run</goal></goals>
<configuration>
<target>
<echo>Cleaning custom dir...</echo>
<delete dir="target/custom" />
</target>
</configuration>
</execution>
<!-- 绑定到 package 阶段 -->
<execution>
<id>package-something</id>
<phase>package</phase>
<goals><goal>run</goal></goals>
<configuration>
<target>
<echo>Packaging custom files...</echo>
<copy todir="target/custom">
<fileset dir="src/main/custom" />
</copy>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
六、常用命令与阶段对应关系
| 命令 | 实际执行的阶段链 |
|---|---|
mvn clean | clean 生命周期:pre-clean → clean |
mvn compile | default:validate → ... → compile |
mvn test | default:validate → ... → compile → test |
mvn package | default:validate → ... → test → package |
mvn install | default:validate → ... → test → package → install |
mvn deploy | default:validate → ... → test → package → install → deploy |
mvn clean install | clean(pre-clean → clean)→ default(validate → ... → install) |
mvn clean install -DskipTests | 跳过 test 阶段中的测试执行(但会编译测试类) |
七、跳过特定阶段的配置
| 需求 | 配置方式 | 说明 |
|---|---|---|
| 跳过测试执行(但编译测试类) | -DskipTests | mvn package -DskipTests,测试类仍会编译 |
| 完全跳过测试(编译+执行都跳过) | -Dmaven.test.skip=true | 更激进,测试代码都不编译 |
| 跳过集成测试 | -DskipITs | 针对 maven-failsafe-plugin |
| 跳过 deploy | -Dmaven.deploy.skip=true | 只 install 不 deploy |
八、自定义生命周期阶段
极少使用,但可以通过 maven-plugin-plugin 自定义新的阶段:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.9.0</version>
<configuration>
<mojoDependencies>
<dependency>com.mycompany:my-custom-plugin</dependency>
</mojoDependencies>
</configuration>
</plugin>
实际开发中几乎不需要自定义生命周期。
九、调试技巧:查看插件绑定关系
1. 查看当前项目的所有阶段绑定
mvn help:describe -Dcmd=compile
2. 查看特定插件的所有 goal
mvn help:describe -DgroupId=org.apache.maven.plugins -DartifactId=maven-compiler-plugin -Ddetail
3. 查看 effective POM 中的插件配置
mvn help:effective-pom | grep -A 20 "plugins"
4. 查看执行时的详细绑定日志
mvn compile -X | grep "binding"
十、核心要点
| 问题 | 答案 |
|---|---|
执行 mvn package 会执行 mvn test 吗? | 会(package 依赖 test,自动先执行) |
执行 mvn install 会执行 mvn package 吗? | 会(install 依赖 package) |
执行 mvn clean 会执行 mvn compile 吗? | 不会(clean 和 default 是不同生命周期) |
mvn compile 的默认插件是什么? | maven-compiler-plugin:compile |
如何让自定义插件在 package 时执行? | 在 <executions> 中配置 <phase>package</phase> |
| 如何查看插件默认绑定到哪个阶段? | mvn help:describe -Dplugin=groupId:artifactId |
-DskipTests 和 -Dmaven.test.skip=true 哪个更彻底? | 后者更彻底(连测试代码都不编译) |
十一、图解执行流程
mvn clean install 的执行过程:
┌─────────────────────────────────────────────────────────────┐
│ clean 生命周期(独立) │
│ pre-clean → clean(删除 target/)→ post-clean │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ default 生命周期(顺序执行) │
│ validate │
│ ↓ │
│ compile ←─── maven-compiler-plugin:compile │
│ ↓ │
│ test ←───── maven-surefire-plugin:test │
│ ↓ │
│ package ←─── maven-jar-plugin:jar │
│ ↓ │
│ install ←─── maven-install-plugin:install │
└─────────────────────────────────────────────────────────────┘
总结:生命周期定义顺序(什么时候做),插件目标执行动作(做什么),绑定把二者连起来。理解这层关系,就能灵活控制 Maven 构建的每个环节。