Maven 生命周期及插件
总结自:《Maven 实战》
一、Maven 生命周期:构建流程的标准化
Maven 通过生命周期将项目构建过程抽象为三个部分,每个部分包含多个有序阶段(Phase),形成标准化的构建流水线。
可以说,生命周期就是对所有构建过程的抽象和统一。其包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。
Maven 的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在 Maven 的设计中,实际的任务(如编译源代码)都交由插件来完成。生命周期抽象了构建的各个步骤,定义了它们的次序,但没有提供具体实现。这种思想与设计模式中的模板方法(Template Method)非常相似。
1. 三大生命周期
Maven 拥有三套相互独立的生命周期,它们分别为 clean、default 和 site。每个生命周期包含一些阶段,这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段,用户和 Maven 最直接的交互方式就是调用这些生命周期阶段。
- clean:清理项目,删除
target目录等构建产物。- 核心阶段:
pre-clean→clean→post-clean
- 核心阶段:
- default(build):核心构建流程,涵盖编译、测试、打包、部署等。
- 关键阶段:
validate→process-sources→compile→test→package→verify→install→deploy process-sources:处理项目主资源文件。一般来说,是对 src/main/resources 目录的内容进行变量替换等工作后,复制到项目输出的主 classpath 目录中。compile:编译项目的主源码。一般来说,是编译 src/main/java 目录下的 Java 文件至项目输出的主 classpath 目录中。test:使用单元测试框架运行测试,测试代码不会被打包或部署。install:将包安装到 Maven 本地仓库,供本地其他 Maven 项目使用。deploy:将最终的包复制到远程仓库,供其他开发人员和 Maven 项目使用。
- 关键阶段:
- site:生成项目站点文档(如项目报告、Javadoc)。
- 核心阶段:
pre-site→site→post-site→site-deploy
- 核心阶段:
2. 生命周期执行逻辑
- 阶段依赖:执行某个阶段时,其之前的所有阶段会被自动执行。
- 例如:
mvn install将依次执行validate到install的所有阶段。
- 例如:
- 独立运行:每个生命周期相互独立,需通过命令指定执行。
- 例如:
mvn install仅会执行validate到install的所有阶段,而不会执行 clean 或 site 中的任何阶段。
- 例如:
二、插件:生命周期的执行引擎
Maven 的所有功能均由插件实现,每个生命周期阶段绑定到具体的插件目标(Plugin Goal),通过插件完成实际任务。
1. 插件与目标的关系
- 插件:一个 Maven 插件可包含多个目标(如
maven-compiler-plugin的compile和testCompile)。 - 目标绑定:生命周期阶段通过插件目标实现功能。
- 例如:
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>
浙公网安备 33010602011771号