Maven 生命周期与插件绑定

一、核心概念

Maven 有三个核心概念,很多人容易混淆:

概念 比喻 说明
生命周期(Lifecycle) 菜谱的"步骤清单" 定义构建过程有哪些阶段(phase),但不关心具体怎么做
阶段(Phase) 菜谱中的"第3步:热锅" 生命周期中的一个节点,有固定顺序
插件目标(Goal) 具体的"烹饪动作" 真正干活的单元,如 javac 编译、jar 打包
绑定(Binding) 把"第3步"绑定到"热锅"这个动作 让某个阶段执行时,自动调用某个插件的目标

总结:生命周期定义"什么时候做",插件定义"做什么",绑定决定"做哪个"。

二、三大生命周期(Lifecycle)

Maven 内置了 3 个独立的生命周期:

生命周期 作用 典型阶段
clean 清理项目 pre-cleancleanpost-clean
default 构建项目(核心) validatecompiletestpackageinstalldeploy
site 生成项目报告/站点 pre-sitesitepost-sitesite-deploy

关键理解

  • 三大生命周期相互独立,执行 mvn clean install 是先跑 clean 生命周期,再跑 default 生命周期。
  • 但 default 内部的 phase 是顺序执行的:执行 mvn package 会自动先执行 validatecompiletest,最后才执行 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 构建的每个环节。