Maven 生命周期及插件

总结自:《Maven 实战》

一、Maven 生命周期:构建流程的标准化

Maven 通过生命周期将项目构建过程抽象为三个部分,每个部分包含多个有序阶段(Phase),形成标准化的构建流水线。

可以说,生命周期就是对所有构建过程的抽象和统一。其包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。

Maven 的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在 Maven 的设计中,实际的任务(如编译源代码)都交由插件来完成。生命周期抽象了构建的各个步骤,定义了它们的次序,但没有提供具体实现。这种思想与设计模式中的模板方法(Template Method)非常相似。

1. 三大生命周期

Maven 拥有三套相互独立的生命周期,它们分别为 clean、default 和 site。每个生命周期包含一些阶段​,这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段,用户和 Maven 最直接的交互方式就是调用这些生命周期阶段。

  • clean:清理项目,删除target目录等构建产物。
    • 核心阶段:pre-cleancleanpost-clean
  • default(build):核心构建流程,涵盖编译、测试、打包、部署等。
    • 关键阶段:validateprocess-sourcescompiletestpackageverifyinstalldeploy
    • process-sources:处理项目主资源文件。一般来说,是对 src/main/resources 目录的内容进行变量替换等工作后,复制到项目输出的主 classpath 目录中。
    • compile:编译项目的主源码。一般来说,是编译 src/main/java 目录下的 Java 文件至项目输出的主 classpath 目录中。
    • test:使用单元测试框架运行测试,测试代码不会被打包或部署。
    • install:将包安装到 Maven 本地仓库,供本地其他 Maven 项目使用。
    • deploy:将最终的包复制到远程仓库,供其他开发人员和 Maven 项目使用。
  • site:生成项目站点文档(如项目报告、Javadoc)。
    • 核心阶段:pre-sitesitepost-sitesite-deploy

2. 生命周期执行逻辑

  • 阶段依赖:执行某个阶段时,其之前的所有阶段会被自动执行。
    • 例如:mvn install将依次执行validateinstall的所有阶段。
  • 独立运行:每个生命周期相互独立,需通过命令指定执行。
    • 例如:mvn install仅会执行validateinstall的所有阶段,而不会执行 clean 或 site 中的任何阶段。

二、插件:生命周期的执行引擎

Maven 的所有功能均由插件实现,每个生命周期阶段绑定到具体的插件目标(Plugin Goal),通过插件完成实际任务。

1. 插件与目标的关系

  • 插件:一个 Maven 插件可包含多个目标(如maven-compiler-plugincompiletestCompile)。
  • 目标绑定:生命周期阶段通过插件目标实现功能。
    • 例如:compile阶段默认绑定到maven-compiler-plugin:compile目标。

2. 默认插件绑定

为了能让用户几乎不用任何配置就能构建 Maven 项目,Maven 为每个生命周期阶段预定义了插件绑定关系:

生命周期阶段 插件目标 执行任务
clean maven-clean-plugin:clean 清理项目构建目录
process-resources maven-resources-plugin:resources 复制主资源文件至主输出目录
compile maven-compiler-plugin:compile 编译主代码至主输出目录
process-test-resources maven-resources-plugin:testResources 复制测试资源文件至测试输出目录
test-compile maven-compiler-plugin:testCompile 编译测试代码至测试输出目录
test maven-surefire-plugin:test 执行单元测试
package maven-jar-plugin:jar 创建项目 jar 包
install maven-install-plugin:install 将项目输出构件安装到本地仓库
deploy maven-deploy-plugin:deploy 将项目输出构件部署到远程仓库
site maven-site-plugin:site 生成项目站点文档
site-deploy maven-site-plugin:site-deploy 将站点文档部署到远程仓库

超级 POM 是所有 Maven 项目的父 POM,所有项目都继承这个超级 POM 的配置,Maven 在超级 POM 中配置了这些核心插件。

上表只列出了拥有插件绑定关系的阶段,还有很多其他阶段,默认它们没有绑定任何插件,因此也没有任何实际行为。

另外 default 生命周期与插件目标的绑定关系相对复杂一些。这是因为对于任何项目来说,例如 jar 项目和 war 项目,它们的项目清理和站点生成任务是一样的,不过构建过程会有区别。例如 jar 项目需要打成 JAR 包,而 war 项目需要打成 WAR 包。

除了默认的打包类型 jar 之外,常见的打包类型还有 war、pom、maven-plugin、ear 等。它们的 default 生命周期与插件目标的绑定关系可参阅 Maven 官方文档:Built-in_Lifecycle_Bind-ings

3. 自定义插件绑定

除了内置绑定以外,用户还能够自己选择将某个插件目标绑定到生命周期的某个阶段上。

一个常见的例子是创建项目的源码 jar 包,内置的插件绑定关系中并没有涉及这一任务,因此需要用户自行配置。maven-source-plugin 可以帮助我们完成该任务,它的 jar-no-fork 目标能够将项目的主代码打包成 jar 文件,可以将其绑定到 default 生命周期的 verify 阶段上,在执行完集成测试后和安装构件之前创建源码 jar 包。

<build>
    <plugins>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>
            <version>2.1.1</version>
            <executions>
                <execution>
                    <id>attach-sources</id>
                    <phase>verify</phase>
                    <goals>
                        <goal>jar-no-fork</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

有时候,即使不通过 phase 元素配置生命周期阶段,插件目标也能够绑定到生命周期中去。例如,删除上述配置中的 phase 一行,再次执行 mvn verify,仍然可以看到 maven-source-plugin:jar-no-fork 得以执行。出现这种现象的原因是:有很多插件的目标在编写时已经定义了默认绑定阶段。

我们知道,当插件目标被绑定到不同的生命周期阶段的时候,其执行顺序会由生命周期阶段的先后顺序决定。如果多个目标被绑定到同一个阶段,它们的执行顺序会是怎样?答案很简单,当多个插件目标绑定到同一个阶段的时候,这些插件声明的先后顺序决定了目标的执行顺序。

4. 插件配置

完成了插件和生命周期的绑定之后,用户还可以配置插件目标的参数,进一步调整插件目标所执行的任务,以满足项目的需求。几乎所有 Maven 插件的目标都有一些可配置的参数,用户可以通过命令行和 POM 配置等方式来配置这些参数。

命令行插件配置

很多插件目标的参数都支持从命令行配置,用户可以在 Maven 命令中使用-D 参数,并伴随一个参数键=参数值的形式,来配置插件目标的参数。

例如,maven-surefire-plugin 提供了一个 maven.test.skip 参数,当其值为 true 的时候,就会跳过执行测试。

mvn install -Dmaven.test.skip=true

POM 中插件全局配置

比如通过pom.xml配置插件参数或覆盖默认绑定:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>11</source>
                <target>11</target>
            </configuration>
        </plugin>
    </plugins>
</build>

上面的配置告诉 maven-compiler-plugin 编译 Java 11 版本的源文件,生成与 JVM 11 兼容的字节码文件。

POM 中插件任务配置

除了为插件配置全局的参数,用户还可以为某个插件任务配置特定的参数。以 maven-antrun-plugin 为例,它有一个目标 run,可以用来在 Maven 中调用 Ant 任务。用户将 maven-antrun-plugin:run 绑定到多个生命周期阶段上,再加以不同的配置,就可以让 Maven 在不同的生命阶段执行不同的任务。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.3</version>
            <executions>
                <execution>
                    <id>ant-validate</id>
                    <phase>validate</phase>
                    <goals>
                        <goal>run</goal>
                    </goals>
                    <configuration>
                        <tasks>
                            <echo>I'm bound to validate phase.</echo>
                        </tasks>
                    </configuration>
                </execution>
                <execution>
                    <id>ant-verify</id>
                    <phase>verify</phase>
                    <goals>
                        <goal>run</goal>
                    </goals>
                    <configuration>
                        <tasks>
                            <echo>I'm bound to verify phase.</echo>
                        </tasks>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

在上述代码片段中,首先,maven-antrun-plugin:run 与 validate 阶段绑定,从而构成一个 id 为 ant-validate 的任务。插件全局配置中的 configuration 元素位于 plugin 元素下面,而这里的 configuration 元素则位于 execution 元素下,表示这是特定任务的配置,而非插件整体的配置。

这个 ant-validate 任务配置了一个 echo Ant 任务,向命令行输出一段文字,表示该任务是绑定到 validate 阶段的。第二个任务的 id 为 ant-verify,它绑定到了 verify 阶段,同样它也输出一段文字到命令行,告诉该任务绑定到了 verify 阶段。

5. 从命令行执行插件

如果在命令行运行 mvn -h 来显示 mvn 命令帮助,可以看到如下的信息:

usage: mvn [options]​ [<goal(s)>] ​[<phase(s)>]

mvn 命令后面可以添加一个或者多个 goal 和 phase,它们分别是指插件目标和生命周期阶段。

我们知道,可以通过 mvn 命令激活生命周期阶段,从而执行那些绑定在生命周期阶段上的插件目标。但 Maven 还支持直接从命令行调用插件目标。Maven 支持这种方式是因为有些任务不适合绑定在生命周期上,例如 maven-help-plugin:describe,我们不需要在构建项目的时候去描述插件信息。

例如mvn help:describe -Dplugin=compiler命令能够查看 maven-compiler-plugin 的描述信息。这其实是如下命令的简写:

mvn org.apache.maven.plugins:maven-help-plugin:2.1:describe -Dplugin=compiler

这里的 mvn 命令,实际上调用了 maven-help-plugin:describe 插件目标,并传入了参数 plugin 的值是 compiler。

6. 插件仓库管理

  • 插件默认从Maven 中央仓库下载,可配置私有仓库加速访问:

    <pluginRepositories>
        <pluginRepository>
            <id>custom-repo</id>
            <url>https://repo.example.com/plugins</url>
        </pluginRepository>
    </pluginRepositories>
    
posted @ 2025-04-11 21:55  Higurashi-kagome  阅读(86)  评论(0)    收藏  举报