三套生命周期

Maven 生命周期的概念大家可能多少都有了解,就算不了解基本上每天也都在用,比如 clean,package,install。这些就是 Maven 生命周期的一个个阶段,但是大家可能不知道的是,Maven 的生命周期一共分了三套,而且是各自独立,互不影响的。

下面就带大家看一下 Maven 的三套生命周期。

clean

首当其冲的就是 clean 生命周期,一共分为三个阶段:pre-clean,clean,post-clean。

一般我们只会使用 clean 这一个阶段,但是要注意,Maven 生命周期阶段之间是有顺序概念的,这个顺序概念会影响 Maven 生命周期执行的效果。为什么这么说呢?因为 Maven 的每一套生命周期的某一个阶段执行时,这个阶段之前的所有阶段都会按照顺序被执行。也就是说,我们每次执行一个生命周期的阶段,并不是只执行这一个阶段,而是它之前所有的阶段都会被执行一遍。这个机制的作用在 default 生命周期体现的尤为明显。

default

平时我们用到最多的生命周期就是 default,主要用到的阶段包括 compile,test,package,install,deploy。

那么为什么执行 test,package 等阶段的时候不需要提前编译项目主代码呢?原因就是上一节提到的生命周期阶段顺序执行的机制,它保证了 compile 会提前自动执行,无需手动操作。

当然,default 生命周期不仅仅只有上面提到的那几个阶段,它的所有阶段按照先后顺序如下图:

maven-08

绝大部分阶段在开发中很少涉及,就不一一介绍了,重点是要记住常用阶段之间的先后顺序。

site

site 这个生命周期在一般的开发中不太会用到,主要是用来生成项目站点文档,内容会包含项目的介绍,依赖信息等。

大部分项目一般情况下不需要项目站点文档,只需提供接口文档即可,可以使用一些更友好、更现代化的工具生成。

site 生命周期阶段主要分为 pre-site,site,post-site,site-deploy。

插件

Maven 的生命周期本身都是一些抽象的概念,具体的执行动作其实都是依赖插件来完成的。通过配置生命周期和插件的绑定关系,开发者在执行 Maven 生命周期阶段的时候,Maven 会自动调用绑定的插件执行具体的插件目标。

Maven 的插件式架构通过核心模块规范整体的业务流程,将具体功能交由各个插件自行实现,最后让用户使用时自由配置,可以达到一种既规范又灵活的状态。

插件目标

前面提到的「插件目标」其实就是插件的一个个功能,一般不会一个功能就开发一个插件,这样既会导致插件泛滥,还会导致很多代码无法复用。因此,通常情况下,一个插件都会包含一组功能,这些功能都是基于一个相同的主题,例如 maven-dependency-plugin,包含依赖相关的所有功能;maven-compiler-plugin,包含编译相关的所有功能。

因为每个插件都不止一个功能,所以在绑定或者执行的时候,不只要指定插件,还需要指定具体的插件目标,才能明确需要执行的指令。

插件绑定

要使得 Maven 的生命周期生效,就需要给生命周期的各个阶段绑定上插件以及插件目标。

为了方便开发者使用,Maven 在基础的几个生命周期阶段都有默认绑定的插件及目标,无需任何配置就能正常使用。比如 clean 生命周期的 clean 阶段绑定了 maven-clean-plugin:clean,default 生命周期的 compile 阶段绑定了 maven-compiler-plugin:compile,default 生命周期的 test 阶段绑定了 maven-surefire-plugin:test。

有默认绑定,就会有自定义绑定,Maven 插件的自定义绑定是在项目的 pom 文件中的 <build> 标签中配置,参考样例如下:

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.10</version>
        <executions>
            <execution>
                <id>copy-dependencies</id>
                <phase>package</phase>
                <goals>
                    <goal>copy-dependencies</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
<plugins>

定义的方式跟依赖类似,也是 groupId,artifactId,version 这三个坐标,通过配置具体的任务,也就是 <execution>,将具体的插件目标绑定到指定的生命周期阶段,样例中就是将 copy-dependencies 这个插件目标绑定到 package 这个生命周期阶段上。

还有一个注意点,当多个插件目标绑定到同一个生命周期阶段的时候,插件声明的先后顺序决定了插件目标的执行顺序。

另外,有很多插件的目标在编写时已经定义了默认绑定阶段,使用时如果没有特定需求可以省去配置绑定阶段这一步。可以通过 Maven 的 help 插件来查看插件目标的默认绑定阶段,参考命令如下:

mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:3.3.0 -Ddetail

输出信息如下:

maven-09

图中的 Bound to phase: package 这一行就表示 jar-no-fork 这个插件目标默认的绑定生命周期阶段是 package。

插件配置

在配置完插件和生命周期阶段的绑定关系之后,开发者还可以对插件进行一些定制化的配置以更好地满足需求。

插件的定制化配置有两种方式,一种是全局配置,声明插件时配置,所有基于该插件的任务都会使用;一种是针对绑定的生命周期阶段单独配置,同样的插件目标在不同的生命周期阶段可以实现不同的效果。

下面是添加了定制化配置的插件配置样例:

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.10</version>
        <configuration>
            <!--是否排除间接依赖包-->
            <excludeTransitive>false</excludeTransitive>
            <!--去除版本信息-->
            <stripVersion>false</stripVersion>
        </configuration>
        <executions>
            <execution>
                <id>copy-dependencies</id>
                <phase>compile</phase>
                <goals>
                    <goal>copy-dependencies</goal>
                </goals>
                <configuration>
                    <!--依赖输出目录-->
                    <outputDirectory>${project.build.directory}/lib</outputDirectory>
                </configuration>
            </execution>
			<execution>
                <id>copy-dependencies-2</id>
                <phase>package</phase>
                <goals>
                    <goal>copy-dependencies</goal>
                </goals>
                <configuration>
					<!--依赖输出目录-->
                    <outputDirectory>${project.build.directory}/lib2</outputDirectory>
                </configuration>
            </execution>
        </executions>
    </plugin>
<plugins>

其中 <excludeTransitive><stripVersion> 是全局配置,<outputDirectory> 是针对生命周期不同阶段的单独配置。

Maven 同时也支持在命令行执行命令的时候指定插件参数,通常都是通过「-D」参数来指定,例如在执行打包时不执行单元测试:mvn package -Dmaven.test.skip=true

几个位置的参数配置生效优先级从高到底分别是:生命周期阶段单独配置 > 全局配置 > 命令行配置。

插件调用

插件的调用除了通过绑定生命周期阶段执行,还可以在命令行直接执行插件目标。例如执行列出项目依赖列表的操作:mvn dependency:list,就是直接调用 maven-dependency-plugin 的 list 目标。

最后

以上就是 Maven 生命周期和插件相关的内容,之前我对于 Maven 插件的概念一直处于一种模模糊糊的状态,通过这次的梳理基本清晰了,也希望能帮助到大家,欢迎大家一起在评论区讨论。