一、前言

Maven可以使用cleancompilepackageinstall等指令完成Java项目构建,一个典型的Maven指令为:mvn package,该命令的控制台输出如下所示:

maven-resources-plugin:2.6:resources (default-resources)
maven-compiler-plugin:3.1:compile (default-compile)
maven-resources-plugin:2.6:testResources (default-testResources)
maven-compiler-plugin:3.1:testCompile (default-testCompile)
maven-surefire-plugin:2.12.4:test (default-test)
maven-jar-plugin:2.4:jar (default-jar)

在执行package指令过程中,分别执行了resourcescompiletestResourcestestCompiletestjar等操作。那么,这里的pacakge指令,和其触发的resources等操作,分别代表了什么,它们之间又有什么关系呢?

二、Maven生命周期阶段

Maven中有三个生命周期(Lifecycle):CleanDefaultSite,一个生命周期中有多个阶段(Phases),生命周期与阶段的对应关系如下:

生命周期(Lifecycle)阶段(Phases)
Cleanclean
Defaultvalidate、initialize、process-sources、compile、process-test-resources、test-compile、test、package、integration-test、verify、install、deploy
Sitesite、site-deploy

上表省略了部分阶段,有关阶段的更多内容详见Maven Lifecycle Reference

mvn命令可以触发一个生命周期阶段,如:mvn package中的package指令参数代表需要执行Default生命周期中的package阶段。同一生命周期中的每一个阶段都依赖于上一阶段,如:package阶段依赖于test阶段,即,执行package阶段之前,肯定会先执行test阶段,以此类推(执行顺序参见上表)。触发生命周期阶段时,其会寻找并执行绑定在该生命周期阶段下的所有执行目标,有关执行目标详见下节。

三、Maven插件中的执行目标

执行目标(Goals)是构建过程中的最小执行单位,它是由Maven插件提供的一个特定的任务。

(一)调用执行目标

执行目标可以直接调用,也可以通过生命周期阶段间接调用。

  1. 直接调用执行目标

maven通过mvn [plugin]:[goal]的方式单独调用一个执行目标。如下所示:

mvn compiler:compile

该命令会调用compiler插件下的compile执行目标,这里的compiler插件是maven-compiler-plugin的缩写,官方插件maven-xxxxxx-plugin都可以缩写成xxxxxx。如果需要指定插件的版本,也可以使用mvn [groupId]:[artifactId]:[version]:[goal]的形式,如下所示:

mvn org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile
  1. 触发生命周期阶段,间接调用绑定在其上的执行目标

maven通过mvn [phase]的方式触发一个生命周期阶段,其会执行所有绑定在该生命周期阶段下的执行目标。如下所示:

mvn compile

该命令会触发compile生命周期阶段,并执行绑定在该生命周期阶段上的所有执行目标。compile生命周期阶段默认绑定了compiler:compile执行目标,因此,在触发compile生命周期阶段时,compiler:compile执行目标也会跟着执行。

(二)生命周期阶段的默认执行目标

每个生命周期阶段都会有默认的执行目标,如下所示:

Phaseplugin:goal
cleanclean:clean
process-resourcesresources:resources
compilecompiler:compile
process-test-resourcesresources:testResources
test-compilecompiler:testCompile
testsurefire:test
packageejb:ejb or ejb3:ejb3 or jar:jar or par:par or rar:rar or war:war
installinstall:install
deploydeploy:deploy

阶段与执行目标的绑定关系详见Maven Built-in Lifecycle Bindings

不管生命周期阶段有没有绑定其它执行目标,默认执行目标一般都会在触发对应生命周期阶段时自动执行。

(三)手动绑定执行目标的生命周期阶段

一个生命周期阶段除了默认的执行目标外,也可以绑定其它执行目标,一个简单示例如下(pom.xml):

<build>
    <plugins>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.7.9</version>
            <executions>
                <execution>
                    <id>report-aggregate</id>
                    <phase>verify</phase>
                    <goals>
                        <goal>report-aggregate</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

该配置将jacoco-maven-plugin插件的report-aggregate执行目标绑定到了verify生命周期阶段上,后续执行mvn verify后,report-aggregate执行目标也会跟着执行。

当然,除了mvn verify外,也可以单独调用mvn org.jacoco:jacoco-maven-plugin:0.7.9:report-aggregate执行对应执行目标。

四、总结

综上,Maven通过生命周期阶段和插件中的执行目标共同完成整个构建流程,执行目标是构建过程中的最小执行单位,生命周期阶段是相关执行目标的集合。

以maven打包为例,在执行mvn package时,其首先会触发Default生命周期的package阶段,依次执行Default中的以下阶段:compile -> test -> package(省略了部分阶段)。在执行这些阶段时,其会找到对应阶段绑定的所有执行目标(如:compile阶段下的compiler:compile执行目标、test阶段下的surefire:test执行目标、package阶段下的jar:jar执行目标、等等),并依次执行。

常用的mvn指令都是通过生命周期阶段来执行对应操作,格式为:mvn [phase],如:通过mvn clean清除编译文件、通过mvn package打包等等,它们的本质都是批量执行了一系列的maven插件执行目标。除此之外,也可以单独执行执行目标,格式为:mvn [plugin]:[goal],如:mvn compiler:compilemvn jar:jar等等。

生命周期阶段会有默认执行目标,如有需要,也可以在pom.xml中通过phase手动指定对应执行目标的生命周期阶段。

参考文档

  1. Maven Build Lifecycle
  2. Maven Plugin Developers Centre