【maven系列1】构建生命周期简介
构建生命周期基础知识
Maven的核心概念是构建生命周期。这意味着构建和分发特定构件(项目)的过程已经明确定义。
对于构建项目的人来说,这意味着只需要学习一小组命令就可以构建任何Maven项目,并且POM将确保他们获得预期的结果。
Maven有三个内置的构建生命周期:default、clean和site。default生命周期处理项目部署,clean生命周期处理项目清理,而site生命周期处理项目网站的创建。
构建生命周期由阶段组成
每个构建生命周期由一系列不同的构建阶段定义,其中构建阶段代表生命周期中的一个阶段。
例如,默认生命周期包括以下阶段(关于生命周期阶段的完整列表,请参考生命周期参考文档):
- validate - 验证项目是否正确,并且所有必要的信息都可用。
- compile - 编译项目的源代码。
- test - 使用适当的单元测试框架对编译好的源代码进行测试。这些测试不需要打包或部署代码。
- package - 将编译好的代码打包成可分发的格式,例如JAR。
- verify - 运行集成测试的结果检查,以确保满足质量标准。
- install - 将包安装到本地仓库,供本地其他项目使用。
- deploy - 在构建环境中将最终的包复制到远程仓库,与其他开发人员和项目共享。
这些构建阶段(加上这里未显示的其他构建阶段)按顺序执行,完成默认生命周期。给定上述构建阶段,这意味着当使用默认生命周期时,Maven将首先验证项目,然后尝试编译源代码,运行单元测试,打包二进制文件(例如jar),运行集成测试,验证集成测试结果,将验证通过的包安装到本地仓库,最后将已安装的包部署到远程仓库。
常用的命令行调用
您应该选择与您期望的结果相匹配的阶段。如果您想要构建jar文件,请运行package。如果您想运行单元测试,请运行test。
如果您不确定您需要什么,首选调用的阶段是
mvn verify
该命令按顺序执行每个默认生命周期阶段(validate、compile、package等),然后执行verify阶段。您只需要调用最后一个要执行的构建阶段,即verify。在大多数情况下,其效果与package相同。但是,**如果有集成测试,这些测试也将被执行。**并且在验证阶段可以执行一些额外的检查,例如检查您的代码是否符合预定义的代码规范。
在构建环境中,使用以下命令来清理地构建和部署构件到共享仓库:
mvn clean deploy
在多模块场景(即具有一个或多个子项目的项目)中,可以使用相同的命令。Maven将进入每个子项目并执行clean,然后执行deploy(包括所有先前构建阶段的步骤)。
构建阶段由插件目标组成
然而,尽管构建阶段负责构建生命周期中的特定步骤,但它执行这些责任的方式可能会有所不同。这是通过声明绑定到这些构建阶段的插件目标来完成的。
插件目标表示对项目的构建和管理做出贡献的特定任务(比构建阶段更精细的任务)。它可以绑定到零个或多个构建阶段。未绑定到任何构建阶段的目标可以通过直接调用在构建生命周期外执行。执行顺序取决于调用目标和构建阶段的顺序。例如,考虑下面的命令。clean和package参数是构建阶段,而dependency:copy-dependencies是插件的一个目标。
mvn clean dependency:copy-dependencies package
如果执行此命令,将首先执行clean阶段(意味着将运行clean生命周期的所有前置阶段,以及clean阶段本身),然后执行dependency:copy-dependencies目标,最后执行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阶段,以收集覆盖率统计信息或关闭集成测试容器。
Failsafe和代码覆盖插件将目标绑定到integration-test和verify阶段。其结果是,在verify阶段之后,可以获得测试和覆盖率报告。如果从命令行调用integration-test,将不会生成报告。更糟糕的是,集成测试容器环境会处于挂起状态;Tomcat Web服务器或Docker实例仍在运行,并且Maven甚至可能无法自动终止。
设置项目以使用构建生命周期
打包方式
构建生命周期的第一种常见方式是通过设置项目的打包方式,使用POM文件中的<packaging>
元素来指定。一些有效的打包方式包括jar、war、ear和pom。如果没有指定打包方式,它将默认为jar。
每个打包方式都包含一个将绑定到特定阶段的目标列表。例如,jar打包方式将以下目标绑定到默认生命周期的构建阶段。
阶段 | plugin:goal for the jar packaging |
---|---|
process-resources | resources:resources |
compile | compiler:compile |
process-test-resources | resources:testResources |
test-compile | compiler:testCompile |
test | surefire:test |
package | jar:jar |
install | install:install |
deploy | deploy:deploy |
这是一个几乎标准的绑定集合;然而,某些打包方式会以不同的方式处理它们。例如,纯粹是元数据的项目(打包方式为pom)只会将目标绑定到install和deploy阶段(关于一些打包类型的完整目标与构建阶段的绑定列表,请参考生命周期参考文档)。
请注意,为了使用某些打包方式,您可能还需要在POM文件的<build>
部分中包含特定的插件,并为该插件指定<extensions>true</extensions>
。一个需要这样做的插件示例是Plexus插件,它提供了plexus-application和plexus-service打包方式。
插件
第二种将目标添加到阶段的方法是在项目中配置插件。插件是提供给Maven的构建工具。此外,一个插件可以有一个或多个目标,其中每个目标代表该插件的一个功能。例如,编译器插件有两个目标:compile和testCompile。前者用于编译主代码的源代码,而后者用于编译测试代码的源代码。
如后面的章节所述,插件可以包含指示将目标绑定到哪个生命周期阶段的信息。请注意,仅添加插件本身是不够的 - 您还必须指定要作为构建一部分运行的目标。
配置的目标将添加到已经绑定到选定打包方式的生命周期的目标中。如果多个目标绑定到特定阶段,执行顺序是首先执行来自打包方式的目标,然后是在POM中配置的目标。请注意,您可以使用<executions>
元素来对特定目标的顺序进行更多控制。
例如,Modello插件默认将其modello:java目标绑定到generate-sources阶段(注意:modello:java目标生成Java源代码)。因此,要使用Modello插件,并使其从模型生成源代码并将其纳入构建中,可以在POM文件的<build>
的<plugins>
部分中添加以下内容:
<plugin>
<groupId>org.codehaus.modello</groupId>
<artifactId>modello-maven-plugin</artifactId>
<version>1.8.1</version>
<executions>
<execution>
<configuration>
<models>
<model>src/main/mdo/maven.mdo</model>
</models>
<version>4.0.0</version>
</configuration>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>
你可能会想知道为什么需要<executions>
元素。这是为了使您可以根据需要多次运行相同的目标,使用不同的配置。单独的执行还可以指定一个ID,以便在继承或应用配置文件时,您可以控制目标配置是合并还是转换为额外的执行。
当给定多个与特定阶段匹配的执行时,它们将按照POM中指定的顺序执行,继承的执行将首先运行。
现在,在modello:java的情况下,它只有在generate-sources阶段才有意义。但是有些目标可以在多个阶段使用,并且可能没有一个合理的默认值。对于这些目标,您可以自己指定阶段。例如,假设您有一个名为display:time的目标,它会将当前时间输出到命令行,您希望它在process-test-resources阶段运行,以指示测试开始的时间。配置如下所示:
<plugin>
<groupId>com.mycompany.example</groupId>
<artifactId>display-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>time</goal>
</goals>
</execution>
</executions>
</plugin>
构建生命周期参考
下面列出了默认、clean和site生命周期的所有构建阶段列表,按照给定的顺序执行,直到指定的阶段为止。
Clean生命周期
阶段 | 描述 |
---|---|
pre-clean | 在实际项目清理之前执行所需的处理过程。 |
clean | 删除上一次构建生成的所有文件。 |
post-clean | 执行完成项目清理所需的处理过程。 |
Default生命周期
阶段 | 描述 |
---|---|
validate | 验证项目是否正确,并且所有必要的信息都可用。 |
initialize | 初始化构建状态,例如设置属性或创建目录。 |
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生命周期绑定
阶段 | 插件:目标 |
---|---|
clean | clean:clean |
Default生命周期绑定 - 打包类型 ejb / ejb3 / jar / par / rar / war
阶段 | 插件:目标 |
---|---|
process-resources | resources:resources |
compile | compiler:compile |
process-test-resources | resources:testResources |
test-compile | compiler:testCompile |
test | surefire:test |
package | ejb:ejb 或 ejb3:ejb3 或 jar:jar 或 par:par 或 rar:rar 或 war:war |
install | install:install |
deploy | deploy:deploy |
Default生命周期绑定 - 打包类型 ear
阶段 | 插件:目标 |
---|---|
generate-resources | ear:generate-application-xml |
process-resources | resources:resources |
package | ear:ear |
install | install:install |
deploy | deploy:deploy |
Default生命周期绑定 - 打包类型 maven-plugin
阶段 | 插件:目标 |
---|---|
generate-resources | plugin:descriptor |
process-resources | resources:resources |
compile | compiler:compile |
process-test-resources | resources:testResources |
test-compile | compiler:testCompile |
test | surefire:test |
package | jar:jar and plugin:addPluginArtifactMetadata |
install | install:install |
deploy | deploy:deploy |
Default生命周期绑定 - 打包类型 pom
阶段 | 插件:目标 |
---|---|
package | |
install | install:install |
deploy | deploy:deploy |
Site生命周期绑定
阶段 | 插件:目标 |
---|---|
site | site:site |
site-deploy | site:deploy |
参考资料
完整的Maven构建生命周期由maven-core模块中的components.xml文件定义,并有相应的文档供参考。
默认生命周期绑定在单独的default-bindings.xml描述符中定义。
请参考Lifecycles Reference和Plugin Bindings for default Lifecycle Reference,以获取来自源代码的最新文档。
- Lifecycle Reference,以获取来自源代码的最新文档。