0%

Maven(三): 生命周期与插件

Maven的生命周期是对项目开发中涉及到的构建过程进行的抽象和统一,其包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署、站点生成等几乎整个构建过程。由于Maven的生命周期是抽象的,所以具体的工作实际上是通过插件去完成的。故本文将把Maven的生命周期与插件放在一起介绍

abstract.png

生命周期

Maven中存在三种生命周期:clean、default、site,分别用于清理项目、构建项目、生成项目站点,而在一个生命周期中通常又会包含若干个阶段

figure 1.jpeg

一个生命周期中的各个阶段是有先后顺序的,后一个阶段任务的执行依赖于前一个阶段任务已经完成。故当用户通过命令行执行某阶段的任务,Maven会自动地把相应的生命周期中的前置阶段任务自动依次执行完成。而三种生命周期之间是相互独立的,执行某一个生命周期的阶段任务时,不会对其他生命周期产生任何影响

用法及说明如下:

1
2
3
4
5
6
mvn phaseName1 [phaseName2]
mvn pre-clean # 只执行 clean 生命周期中的 pre-clean 阶段
mvn clean # 依次执行 clean 生命周期中的 pre-clean,clean 阶段
mvn compile # 依次执行 default 生命周期中的 validate,initialize,...,process-resources,compile 阶段
mvn clean compile # 依次执行 clean 生命周期中的 pre-clean,clean 阶段, default 生命周期中的 validate,initialize,...,process-resources,compile 阶段
mvn clean site # 依次执行 clean 生命周期中的 pre-clean,clean 阶段, site 生命周期中的 pre-site,site 阶段

Maven插件

插件目标

如前文所述,Maven的生命周期是抽象的,各阶段的工作实际是通过Maven插件去执行完成的。在一个Maven插件中,其可以具备多个功能,通常我们称插件功能为Goal目标。以 maven-dependency-plugin 插件为例进行说明,我们已知的功能有:查看依赖列表、以树形结构图查看依赖、分析依赖。相应地,其插件目标分别为dependency:list、dependency:tree、dependency:analyze。在插件目标的写法中,冒号前为该插件的插件前缀,冒号后为该插件的目标

绑定插件

将Maven生命周期与Maven插件相互进行绑定,即可完成实际的构建任务。具体地,是将生命周期的阶段与插件的插件目标相互绑定,以完成某个具体的构建任务。以项目编译为例,将default生命周期的compile阶段与maven-compiler-plugin插件的compile目标绑定在一起后,即可实现项目编译

1. 内置绑定

由于Maven的生命周期需要绑定插件后才可以产生实际作用。为最大程度降低用户的配置、使用难度,Maven对各生命周期的主要阶段绑定了默认插件目标。基于此用户无需手动绑定,直接在命令行中调用Maven的相关生命周期阶段即可执行相应的构建任务。Maven对各生命周期的内置绑定如下,由于default生命周期阶段内置绑定的插件目标与最终打包的类型有关,这里以常见的打包类型jar包为例进行介绍

figure 2.jpeg

2. 自定义绑定

Maven内置绑定的插件可供用户实现基础的项目构建任务,而如果用户需要完成其他的构建任务时,可通过自定义绑定的方式将某个插件目标绑定到生命周期的某个阶段上。这里我们以创建项目的源码jar包举例说明,由于内置绑定的插件目标没有可以完成该任务的,所以我们需要先确定可以完成该任务的Maven插件及插件目标,然后将其绑定到生命周期的某一阶段上

插件 maven-source-plugin 的 jar-no-fork 目标能够将项目的主代码打包为jar包。在项目POM文件build元素的子元素plugins中可包含若干个plugin元素,其可用于向项目中引入Maven的插件。Maven插件和Maven依赖一样也是基于Maven进行管理的,故其同样需要配置groupId、artifactId、version元素信息。executions元素下可包含若干个execution子元素,用于配置执行任务。这里我们配置一个id为attach-sources的任务,将该插件的目标 jar-no-fork 与default生命周期的package阶段进行绑定。实例代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<build>
...
<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>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>

至此,创建项目的源码jar包的任务即可通过执行下述命令实现

1
mvn package

构建后生成的jar源码包如图中绿框所示。当多个插件目标被绑定到生命周期的同一个阶段时,其执行顺序将由其插件目标的声明顺序决定。本例中package阶段绑定了2个任务,一个是内置绑定的打包任务,一个是自定义绑定的jar源码包任务,其执行顺序如下图红框所示

figure 3.jpeg

命令行调用插件

在命令行中执行mvn -h可查看mvn命令的格式。其中phase参数指生命周期中的阶段,我们可以在命令行中使用阶段参数来执行绑定在相应阶段上的插件目标(e.g., mvn clean package),该用法已在上文介绍过。此处将不再多言。这里我们着重介绍goal参数——插件目标,即我们可以在命令行直接调用插件目标,因为不是所有插件目标都适合绑定到生命周期的阶段上,例如上文介绍的maven-dependency-plugin插件的dependency:list、dependency:tree、dependency:analyze等目标

1
mvn [options] [<goal(s)>] [<phase(s)>]

插件前缀

上文简要介绍了可以通过mvn命令直接调用插件目标,语法如下所示

1
2
3
# 通过Maven坐标调用插件
mvn [groupId]:[artifactId]:[version]:[goalName]
mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:list

上述语法的弊端在于命令过长不够简洁。为便于使用,Maven对插件引入了插件前缀的概念,用来代替groupId、artifactId信息。所以一个Maven插件不仅可通过Maven坐标(groupId、artifactId)还可以通过插件前缀进行描述。使用插件前缀可大大简化命令,语法如下所示。例如 maven-dependency-plugin插件的插件前缀是dependency,则查看依赖列表,可使用下述命令实现

1
2
3
# 通过插件前缀调用插件
mvn [pluginPrefix]:[goalName]
mvn dependency:list

获取插件信息

日常开发中,我们经常需要了解一个插件的帮助信息,这个时候就可以通过maven-help-plugin插件的describe目标来实现。常见用法如下。其中,plugin参数用于指定欲查询的插件(Maven坐标、插件前缀均可),goal参数用于指定该插件的目标,detail参数可输出更详细的信息

1
2
3
4
5
# 查看指定插件的信息
mvn help:describe -Dplugin=[groupId]:[artifactId]:[version] [-Ddetail]
mvn help:describe -Dplugin=[pluginPrefix] [-Ddetail]
# 查看指定插件的某个目标的信息
mvn help:describe -Dplugin=[pluginPrefix] -Dgoal=[goalName] [-Ddetail]

这里以我们在上文自定义绑定的 maven-source-plugin 插件为例,来获取该插件的帮助信息。结果如下所示,蓝框所示的为该插件的基本信息,包含描述、Maven坐标、插件前缀(source)等信息。下面则列出了该插件的所有的目标。其中黄框所示的插件目标source:jar-no-fork,即为上文提到的用于创建项目的源码jar包

figure 4.jpeg

使用detail参数会显示该插件每个目标的详细信息,致使输出的信息非常多。而很多时候,我们只需要我们关注的目标的详细信息即可。这时即可通过goal参数指定插件的目标,这里我们同样以 maven-source-plugin 插件为例,查看目标jar-no-fork的详细信息。结果如下所示,黄框为插件目标。还记得我们之前在配置自定义绑定时,是通过设置execution元素下的phase元素来绑定到生命周期的阶段上的。如果用户未配置phase元素,将会使用蓝框所示的默认值进行绑定

figure 5.jpeg

Note:

  • 在Java中,参数-D用于设置Java系统属性。这里Maven借用了这一参数,所以可以看到Maven命令中大部分参数前都有-D标识

插件配置

命令行插件配置

一般情况下插件的目标中存在若干个参数用于改变插件的行为,用户在命令行中可通过参数 Parameters用户属性 User Property进行配置。这里以default生命周期的test阶段默认绑定的 maven-surefire-plugin 插件的test为例进行说明

执行下述命令查看插件目标 surefire:test 的详细信息

1
mvn help:describe -Dplugin=surefire -Dgoal=test -Ddetail

figure 6.jpeg

从上图红框中可以看出test目标下有参数skip、skipTests。根据绿框中的描述信息可知,当前者为true时,该目标执行时不会编译测试代码也不会执行单元测试用例;当后者为true时,该目标执行时虽然不会执行单元测试用例但其会对测试代码进行编译。蓝框为该参数的用户属性,可在命令行设置用户属性来改变插件的行为。例如我们在构建项目时,有时候为了节约项目构建时间,希望跳过单元测试,即不运行单元测试用例,即可通过在命令后添加相关属性的配置即可,如下所示

figure 7.jpeg

POM中插件的全局配置

上面我们介绍了如何在命令行配置插件来改变插件行为,而更多的时候我们需要在项目的POM文件中配置插件。一方面是因为在命令行中配置插件实际上是通过插件目标参数的用户属性实现的,但是有些插件参数并没有用户属性故无法在命令行中进行配置;另一方面,插件常用的配置写在POM中总比命令行一次一次敲来的省事的多。具体地,可在POM文件中的plugin元素的子元素configuration中配置插件参数 Parameters。由于该参数是直接配置到插件中并未指定具体的插件目标,所以其是一个全局配置会对该插件下的所有目标生效

下面即是一个对 maven-compiler-plugin 插件的全局配置示例,在POM中配置该插件中的参数source、tagret值为1.8——使用Java 1.8版本的编译器对源码进行编译、生成与JVM 1.8版本兼容的class文件,则该POM中的配置对于插件中存在相关参数的插件目标compiler:compile、compiler:testCompile将均生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<build>
...
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
...
</plugins>
...
</build>

POM中插件的任务配置

在POM中配置插件参数,不仅支持全局配置,还可以基于任务来配置插件参数,实现在不同任务中使用不同的插件配置。虽然这里也是在POM文件的configuration元素配置插件参数,但是需要注意的是这里configuration元素是位于用于配置任务的execution元素下的。下面即是一个对 maven-compiler-plugin 插件 testCompile 目标的配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<build>
...
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<id>compile-test-code</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>

参考文献

  1. Maven实战 许晓斌著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝