Maven 生命周期及其阶段
Maven 基于生命周期的核心概念。有三个内置的生命周期:
-
clean
:负责清理项目; -
default
:负责构建项目; -
site
:负责建立项目站点。
每个生命周期都包含一些阶段(phase
),用户与 Maven 最直接的交互方式就是调用这些生命周期的阶段。
clean 生命周期及其阶段
clean
生命周期包含以下阶段:
-
pre-clean
:在清理之前完成一些所需的工作; -
clean
:删除之前构建出的所有文件; -
post-clean
:在清理之后完成一些所需的工作。
default 生命周期及其阶段
default
生命周期包含了实际构建时需要执行的所有步骤,是最重要的生命周期。该生命周期包含以下阶段:
-
validate
:验证项目是否正确,所有必要信息是否可用; -
initialize
:初始化构建状态,例如设置 property 或创建目录; -
generate-sources
:生成要包含在编译中的任何源代码; -
process-sources
:处理源代码,例如替换所有引用的值; -
generate-resources
:生成要包含在包中的资源; -
process-resources
:将资源复制并处理到目标目录中,准备打包; -
compile
:编译项目的源代码; -
process-classes
:对编译生成的文件进行后期处理,例如对 Java 类进行字节码增强; -
generate-test-sources
:生成要包含在编译中的任何测试源代码; -
process-test-sources
:处理测试源代码,例如替换所有引用的值; -
generate-test-resources
:为测试创建资源; -
process-test-resources
:将资源复制并处理到测试目标目录中; -
test-compile
:将测试源代码编译到测试目标目录中; -
process-test-classes
:对测试编译生成的文件进行后期处理,例如对 Java 类进行字节码增强; -
test
:使用合适的单元测试框架运行测试。这些测试不应要求打包或部署代码; -
prepare-package
:在实际打包之前,执行准备打包所需的任何操作。这通常会生成已处理但尚未打包的所有文件和目录; -
package
:获取编译后的代码,并将其打包为可分发的格式,如 JAR; -
pre-integration-test
:在执行集成测试之前执行所需的操作。这可能涉及一些事项,如设置所需环境等; -
integration-test
:如有必要,处理包并将其部署到可以运行集成测试的环境中; -
post-integration-test
:执行集成测试后执行所需的操作。这可能包括清理环境; -
verify
:运行任何检查以验证包是否有效并符合质量标准; -
install
:将包安装到本地仓库中,作为本地其他项目中的依赖项使用; -
deploy
:在集成或发布环境中完成,将最终包复制到远程仓库,以便与其他开发人员和项目共享。
site 生命周期及其阶段
-
pre-site
:在生成实际的项目站点之前执行所需的流程; -
site
:生成项目的站点文档; -
post-site
:执行完成站点生成所需的流程; -
site-deploy
:将生成的站点文档部署到指定的 web 服务器。
常见的命令行调用
每个生命周期内的阶段都是有顺序的,且后面的阶段依赖于前面的阶段。当调用某个生命周期的某个阶段时,则会尝试先依次调用该生命周期内位于该阶段前面的各个阶段。比如,当调用 clean
生命周期的 pre-clean
阶段时,仅有 pre-clean
阶段得以执行;当调用 clean
生命周期的 clean
阶段时,则会依次执行 pre-clean
、clean
阶段;当调用 clean
生命周期的 post-clean
阶段时,则会依次执行 pre-clean
、clean
、post-clean
阶段。
这三个生命周期彼此都是独立的,用户可以仅调用 clean
生命周期的某个阶段,或者仅仅调用 default
生命周期的某个阶段,而不会对其他生命周期产生影响。
如果想要 jar 包,请运行 mvn package
。如果要运行单元测试,请运行 mvn test
。
如果你不确定你想要什么,那就调用 mvn verify
命令。在 verify
阶段执行验证之前,将按顺序执行 default
生命周期中在 verify
阶段之前的其他每个阶段(validate
、compile
、package
等),即只需要调用要执行的最后一个构建阶段即可。在大多数情况下,调用 verify
的效果与 package
相同。但是,如果存在集成测试,也将执行这些阶段。在 verify
阶段,可以进行一些额外的检查。例如,如果您的代码是根据预定义的 checkstyle 规则编写的。
在构建环境中,使用 mvn clean deploy
以下调用来干净地构建工件并将其部署到共享仓库中。同一命令可用于多模块场景(即具有一个或多个子项目的项目),Maven 将遍历每个子项目并执行 clean
,然后执行 deploy
(包括所有先前的构建阶段步骤)。
Maven 插件
概述
Maven 的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,实际的任务都是依靠插件来完成。每个构建步骤都可以绑定一个或多个插件行为,Maven 为大多数构建步骤编写并绑定了默认插件。当用户有特殊需要的时候,可以自己配置插件来定制构建行为,甚至可以使用自己编写的 Maven 插件。
插件目标及其与阶段的绑定
插件往往能够完成多个任务。比如,maven-dependency-plugin
插件能够基于项目依赖做很多事情,它能够分析项目依赖来帮助找出潜在的无用依赖,它能够列出项目的依赖树来帮助分析依赖来源,它能够列出项目所有已解析的依赖等等。插件里的每项功能就是一个插件目标(plugin goal
)。上述提到的几个功能分别对应的插件目标为dependency:analyze
、dependency:tree
和 dependency:list
。这是一种通用的写法,冒号前面是插件前缀,冒号后面是该插件的目标。类似地,还可以写出 compiler:compile
(这是 maven-compiler-plugin
的 compile
目标)和 surefire:test
(这是 maven-surefire-plugin
的 test
目标)。
Maven 生命周期中的阶段与插件目标相互绑定,用以完成实际的构建任务。插件目标可能绑定到零个或多个构建阶段。未绑定到任何阶段的插件目标可以通过直接调用从而在生命周期之外执行。执行顺序取决于调用插件目标和阶段的顺序。例如,下面的命令中的 clean
和 package
参数是构建阶段,而dependency:copy-dependencies
是插件的目标。
mvn clean dependency:copy-dependencies package
如果要执行上面的操作,将首先执行 clean
阶段(这意味着它将运行 clean
生命周期中位于 clean
阶段之前的所有阶段,加上 clean
阶段本身),然后执行 dependency:copy-dependencies
插件目标,最后执行 package
阶段(以及 default
生命周期中位于 package
阶段之前的所有阶段)。
如果一个插件目标绑定到一个或多个阶段,那么将在所有这些阶段中调用该插件目标。此外,某个阶段也可以有零个或多个插件目标。如果某个阶段没有绑定插件目标,则该阶段将不会执行,但如果该构建阶段有一个或多个插件目标,则该构建阶段将执行所有这些插件目标。
注意:在 Maven 2.0.5 及更高版本中,绑定到一个阶段的多个插件目标的执行顺序与 POM 中插件目标的声明顺序相同,但是不支持同一插件的多个实例。在 Maven 2.0.11 及更高版本中,同一插件的多个实例按分组来一起执行和排序。
不从命令行调用的阶段
用连字符(pre-*
、post-*
、或 process-*
)命名的阶段通常不会从命令行直接调用。这些阶段对构建进行排序,在构建之外生成没有用处的中间结果。比如,在调用 integration-test
时,环境可能处于挂起状态。
代码覆盖工具(如 Jacoco)和容器插件(如 Tomcat、Cargo 和 Docker)将目标绑定到 pre-integration-test
阶段,以准备集成测试的容器环境。这些插件还将目标绑定到 post-integration-test
阶段,以收集覆盖率统计信息或停用集成测试容器。
故障保护和代码覆盖插件将目标绑定到 integration-test
和 verify
阶段。最终结果是测试和覆盖率报告在 verify
阶段之后可用。如果非要从命令行调用 integration-test
,则不会生成任何报告。更糟糕的是,集成测试容器环境处于挂起状态(空跑),Tomcat WebServer 或 Docker 实例保持运行,Maven 甚至可能不会自行终止。
内置绑定
为了能让用户几乎不用任何配置就能构建 Maven 项目,Maven 为一些主要的生命周期阶段绑定了很多插件目标,当用户通过命令行调用这些内置绑定好的阶段时,对应的插件目标就会执行相应的任务。Maven 在 META-INF/plexus/components.xml
文件中定义了 3 个生命周期及其绑定。
clean 生命周期中各阶段绑定的插件目标
Maven 在 META-INF/plexus/components.xml
中关于 clean
生命周期中各阶段及其绑定的目标如下:
pre-clean
clean
post-clean
org.apache.maven.plugins:maven-clean-plugin:2.5:clean
default 生命周期中各阶段绑定的插件目标
Maven 在 META-INF/plexus/components.xml
中关于 default
生命周期及其绑定的目标如下:
validate
initialize
generate-sources
process-sources
generate-resources
process-resources
compile
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources
test-compile
process-test-classes
test
prepare-package
package
pre-integration-test
integration-test
post-integration-test
verify
install
deploy
default 生命周期的定义没有绑定任何插件,插件绑定在 META-INF/plexus/default-bindings.xml
文件中单独定义,因为每个打包类型都不同。
pom 打包所绑定的插件目标
在 META-INF/plexus/default-bindings.xml
文件中关于 pom
打包类型的默认绑定如下:
org.apache.maven.plugins:maven-install-plugin:2.4:install
org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
jar 打包所绑定的插件目标
在 META-INF/plexus/default-bindings.xml
文件中关于 jar 打包类型的默认绑定如下:
org.apache.maven.plugins:maven-resources-plugin:2.6:resources
org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
org.apache.maven.plugins:maven-jar-plugin:2.4:jar
org.apache.maven.plugins:maven-install-plugin:2.4:install
org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
ejb 打包所绑定的插件目标
在 META-INF/plexus/default-bindings.xml
文件中关于 ejb
打包类型的默认绑定如下:
org.apache.maven.plugins:maven-resources-plugin:2.6:resources
org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
org.apache.maven.plugins:maven-ejb-plugin:2.3:ejb
org.apache.maven.plugins:maven-install-plugin:2.4:install
org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
maven-plugin 打包所绑定的插件目标
org.apache.maven.plugins:maven-resources-plugin:2.6:resources
org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
org.apache.maven.plugins:maven-plugin-plugin:3.2:descriptor
org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
org.apache.maven.plugins:maven-jar-plugin:2.4:jar,
org.apache.maven.plugins:maven-plugin-plugin:3.2:addPluginArtifactMetadata
org.apache.maven.plugins:maven-install-plugin:2.4:install
org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
war 打包所绑定的插件目标
在 META-INF/plexus/default-bindings.xml
文件中关于 war
打包类型的默认绑定如下:
org.apache.maven.plugins:maven-resources-plugin:2.6:resources
org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
org.apache.maven.plugins:maven-war-plugin:2.2:war
org.apache.maven.plugins:maven-install-plugin:2.4:install
org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
ear 打包所绑定的插件目标
在 META-INF/plexus/default-bindings.xml
文件中关于 ear
打包类型的默认绑定如下:
org.apache.maven.plugins:maven-ear-plugin:2.8:generate-application-xml
org.apache.maven.plugins:maven-resources-plugin:2.6:resources
org.apache.maven.plugins:maven-ear-plugin:2.8:ear
org.apache.maven.plugins:maven-install-plugin:2.4:install
org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
rar 打包所绑定的插件插件目标
在 META-INF/plexus/default-bindings.xml
文件中关于 rar
打包类型的默认绑定如下:
org.apache.maven.plugins:maven-resources-plugin:2.6:resources
org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
org.apache.maven.plugins:maven-rar-plugin:2.2:rar
org.apache.maven.plugins:maven-install-plugin:2.4:install
org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
site 生命周期中各阶段绑定的插件目标
Maven 在 META-INF/plexus/components.xml
中关于 site 生命周期中各阶段及其绑定的目标如下:
pre-site
site
post-site
site-deploy
org.apache.maven.plugins:maven-site-plugin:3.3:site
org.apache.maven.plugins:maven-site-plugin:3.3:deploy
配置插件
在Maven中,有两种插件,构建和报告:
- 构建插件在构建期间执行,并在
元素中配置。 - 报告插件在站点生成期间执行,并在
元素中配置。
所有插件都应该具有最少的必需信息:groupId
、artifactId
和 version
。建议始终定义每个插件的版本,以保证构建的可复制性。一个好的做法是在
元素中指定每个构建插件的版本。对于报告插件,请在
元素中指定每个插件版本。通常会在父级 POM 中定义一个
元素来统一管理各插件的版本。
一般配置
Maven 插件(构建插件和报告插件)通过指定
元素进行配置,其中
元素的子元素映射到 Mojo 类中的字段或 setter。Mojo 是 Maven plain Old Java Object
的缩写,Mojo 映射到一个插件目标,插件由一个或多个 Mojo 组成。例如,一个 Mojo 使用指定的超时时间和选项列表对特定的 URL 执行查询。Mojo 的定义可能如下所示:
/**
* @goal query
*/
public class MyQueryMojo extends AbstractMojo {
@Parameter(property = "query.url", required = true)
private String url;
@Parameter(property = "timeout", required = false, defaultValue = "50")
private int timeout;
@Parameter(property = "options")
private String[] options;
public void execute() throws MojoExecutionException {
...
}
}
要使用所需的 URL、超时时间和选项,则需要从 POM 配置该 Mojo:
...
maven-myquery-plugin
1.0
http://www.foobar.com/query
10
...
配置中的元素与 Mojo 中的字段名称相匹配。映射机制可以通过检查字段的类型并确定是否可能进行适当的映射来处理数组。映射是直接进行的。比如,url 元素映射到 url 字段,timeout 元素映射到 timeout 字段,options 元素映射到 options 字段。
对于打算直接从 CLI 执行的 Mojo,其参数通常提供通过系统属性(Java 的 -D
选项可用于提供系统属性)而不是通过 POM 中的
部分进行配置。这些参数的插件注解将列出对应系统属性的表达式。在上面的 Mojo 中,参数 url
与表达式 ${query.url}
相关联,这意味着它的值可以由系统属性 query.url
指定,如下所示:
mvn myquery:query -Dquery.url=http://maven.apache.org
系统属性的名称不一定与 mojo 参数的名称匹配。虽然这是一种相当常见的做法,但经常会有插件为系统属性使用了一些前缀,以避免名称与其他系统属性冲突。也可能有少数插件参数使用与参数名称完全无关的系统属性(例如出于历史原因),因此,请务必仔细阅读插件文档。
help 目标
大多数 Maven 插件都有一个 help
目标,即打印插件及其参数和类型的描述。例如,要查看 javadoc 目标的帮助,请键入:
mvn javadoc:help -Ddetail -Dgoal=javadoc
配置参数
映射简单类型
映射简单类型(如布尔或整数)非常简单。
元素可能如下所示:
...
a string
true
10
1.0
c:\temp
http://maven.apache.org
...
映射复杂对象
映射复杂类型也相当简单。让我们看一个简单的示例,其中我们尝试映射 Person 对象的配置。
元素可能如下所示:
...
Jason
van Zyl
...
映射复杂对象的规则如下所示:
必须有一个私有字段对应于要映射的元素的名称。因此在我们的例子中,person 元素必须映射到 Mojo 中的 person 字段。
实例化的对象必须与 Mojo 本身在同一个包中。因此,如果您的 Mojo 位于
com.mycompany.mojo.query
中,那么映射机制将在该包中查找名为 Person 的对象。该机制将元素名称的第一个字母大写,并使用该名称搜索要实例化的对象。-
如果希望将要实例化的对象放在另一个包中,或使用更复杂的名称,请使用以下实现属性指定该名称:
...
Jason van Zyl
配置映射机制可以轻松地处理大多数集合,因此让我们通过几个示例向您展示它是如何完成的:
映射 List 类型
映射列表的工作方式与映射到数组的工作方式大致相同,在数组中,元素列表将映射到列表。因此,如果你有一种如下的 Mojo:
public class MyAnimalMojo extends AbstractMojo {
@Parameter(property = "animals")
private List animals;
public void execute()
throws MojoExecutionException
{
...
}
}
如果有一个名为 animals 的字段,则插件的配置如下所示:
...
maven-myanimal-plugin
1.0
cat
dog
aardvark
...
其中列出的每种动物都是 animals
字段中的条目。与数组不同,集合没有特定的组件类型。为了推导出列表项的类型,使用以下策略:
- 如果 XML 元素包含
implementation
提示属性,则使用该属性; - 如果 XML 元素包含
.
号,将尝试将其作为完全限定的类名; - 尝试将 XML 标记(首字母大写)作为与正在配置的 mojo/object 所在包中的类;
- 如果 XML 元素没有内容,则假定其类型为
String
。否则,配置将失败。
映射 Map 类型
同样,您可以按如下方式定义 Map:
...
@Parameter(property = "myMap")
private Map myMap;
...
...
value1
value2
...
映射 Properties 类型
Properties 的定义应如下所示:
...
@Parameter(property = "myProperties")
private Properties myProperties;
...
...
propertyName1
propertyValue1
propertyName2
propertyValue2
...
配置构建插件
以下仅用于在
元素中配置构建插件。
使用
元素
您还可以使用
元素配置 Mojo。这最常用于打算参与构建生命周期的某些阶段的 Mojo。以 MyQueryMoo
为例,您可能会看到如下内容:
...
maven-myquery-plugin
1.0
execution1
test
http://www.foo.com/query
10
query
execution2
http://www.bar.com/query
15
query
...
id 为 execution1
的第一个
元素将此配置绑定到 test
阶段。
第二个
元素没有
子元素,将会如何执行呢?目标可以有一个默认的阶段绑定,通过 Mojo 类的 @phase
注释来指定。如果目标有一个默认的阶段绑定,那么它将在该阶段执行。但是如果目标没有绑定到任何生命周期阶段,那么它就不会在构建生命周期中执行。
请注意,虽然某个
元素的 id 必须在 POM 中单个插件的所有
元素中都是唯一的,但它们不必在 POM 的继承层次结构中也是唯一的,将会合并来自不同 POM 的相同 id 的
元素。这同样适用于由 profile 定义的
元素。
如果我们有一个绑定了不同阶段的多个
元素,将会如何执行呢?让我们再次使用上面的示例 POM,但这次我们将把 id 为 execution2
的
元素绑定到一个阶段。
...
...
execution1
test
...
execution2
install
http://www.bar.com/query
15
query
...
如果有多个
元素绑定到不同的阶段,那么 Mojo 将针对指定的每个阶段都执行一次。也就是说,当构建阶段为 test
阶段时,将应用
中的设置来执行 execution1
,当构建阶段已处于 install
阶段时,将应用
中的设置来执行 execution2
。
现在,让我们来看另一个 Mojo 示例,它显示了 default 生命周期阶段绑定。
/**
* @goal query
* @phase package
*/
public class MyBoundQueryMojo extends AbstractMojo {
@Parameter(property = "query.url", required = true)
private String url;
@Parameter(property = "timeout", required = false, defaultValue = "50")
private int timeout;
@Parameter(property = "options")
private String[] options;
public void execute() throws MojoExecutionException {
...
}
}
在上面的 Mojo 示例中,MyBoundQueryMojo
默认绑定到 package
阶段(请参见 Mojo 类的 @phase
注释)。但是如果我们想在 install
阶段而不是在 package
执行这个 Mojo,我们可以使用
下的
子元素来将 Mojo 重新绑定到一个新的生命周期阶段。
...
maven-myquery-plugin
1.0
execution1
install
http://www.bar.com/query
15
query
...
现在,MyBoundQueryMo
默认阶段已经从 package
阶段被 install
阶段所覆盖。
注意:
元素内部的配置与
外部的配置不同,
内部的配置不能从直接命令行调用中使用,它们仅在所绑定的阶段被调用时应用。因此,如果想将
部分全局应用于插件的所有调用,则需要将其移到
之外。
从 Maven 3.3.1 开始,您可以在命令行上指定插件目标所调用的
的 id。因此,如果要从命令行运行上述插件及其特定的 execution1
配置,可以执行:
mvn myqyeryplugin:queryMojo@execution1
使用
元素
您可以配置构建插件的依赖项,通常使用较新的依赖项版本。例如,Maven Antrun 插件版本 1.2 使用 1.6.5 版本的 Ant ,如果您想在运行此插件时使用最新的Ant 版本,则需要添加
元素,如下所示:
...
org.apache.maven.plugins
maven-antrun-plugin
1.2
...
org.apache.ant
ant
1.7.1
org.apache.ant
ant-launcher
1.7.1
...
在构建插件中使用
元素
默认情况下,插件配置应该传播到子 POM,因此要中断继承,可以将
元素值设置为 false
:
...
org.apache.maven.plugins
maven-antrun-plugin
1.2
false
...
...
配置报告插件
以下仅用于在
元素中配置报告插件。
在 pom 中的
元素与
元素中配置报告插件的行为不同。
mvn site
仅使用
元素中指定的每个报告插件的
元素中定义的参数,即 site
始终忽略
中指定的每个插件的
元素中定义的参数。
mvn aplugin:areportgoal
首先使用
元素中指定的每个报告插件的
元素中定义的参数;如果未找到参数,它将查找在
中指定的每个插件的
元素中定义的参数。
使用
元素
可以使用
元素配置报告插件。在运行 mvn site
时,最常用于有选择地生成报告。以下内容将仅生成项目团队报告。
...
org.apache.maven.plugins
maven-project-info-reports-plugin
2.1.2
project-team
...
要排除所有报告,则
元素中不要设置任何值:
参考每个插件文档(比如 plugin-info.html)来了解可用的报告目标。
在报告插件中使用 <inherited>
标记
默认情况下,插件配置应该传播到子 POM,因此要中断继承,可以将
元素值设置为 false
:
...
org.apache.maven.plugins
maven-project-info-reports-plugin
2.1.2
false
...