文章目录

1.前言2.生命周期2.1.生命周期的阶段2.2.插件目标2.3.其他插件绑定2.3.1.源码生成插件2.3.2.SpringBoot打包插件

3.总结

1.前言

本系列文章记录了 Maven 从0开始到实战的过程,Maven 系列历史文章清单:

(一)5分钟做完 Maven 的安装与配置 (二)使用 Maven 创建并运行项目、聊聊 POM 中的坐标与版本号的规则 (三)Maven仓库概念及私服安装与使用 附:Nexus安装包下载地址 (四)图解Maven3依赖的功能特性:依赖范围、依赖传递、依赖冲突 (五)Maven模块的继承与聚合 多模块项目组织构建

在之前的内容中,我们使用了一个简单的 demo 来验证 Maven中的依赖传递、继承、聚合等特性,在构建demo的时候,已经在不知情的情况下,使用过了生命周期与插件。

在验证的过程中,多次使用到了一个指令 mvn clean install ,我们已经知道了 clean 是清理当前项目的 target 目录,install 是将当前项目打包并推送到本地仓库中去。这里的 clean 与 install 就是 Maven 的生命周期中的某个阶段,在每个阶段实际完成工作的就是插件。

可能这段话比较抽象,没关系,接下来会详细的讲解,先看一本篇的主要内容:

Maven 生命周期详解:

生命周期中的 阶段 与 插件目标常见的生命周期指令插件与生命周期绑定使用

有一定的Maven经验和英文阅读能力的同学,建议看看官方文档。附:《Maven生命周期官方文档》

2.生命周期

我们在开发中描述的项目的生命周期,一般是指的是 编译、测试、打包、部署等过程,每个项目的构建生命周期或多或少都有一些差异,Maven 对构建的过程进行的抽象和统一,提出了 Maven生命周期这一个抽象的概念,它的作用是定义一条执行流程,而不会完成实际的工作,在每个流程节点中的工作都会交给具体实例对象去完成。

这个执行的流程中包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署、站点生成等步骤,几乎覆盖了项目构建中的所有流程节点,我们可以按需选择其中的一部分步骤生成自己的项目包。

Maven 对流程、流程节点、具体工作都有专用的名词,如下:

流程:生命周期(lifeCycle)流程节点:阶段 (phase)具体工作:插件目标 (plugin:goal)

Maven 的生命周期有三个,分别是clean、default、site,其中 site 使用的较少,另外两个都是经常会使用到的。这三个生命周期彼此是隔离开的个体,它们可以单独运行,也可以组合起来运行,例如:

# 单独清理

mvn clean

# 单独构建

mvn package

mvn install

mvn deploy

# 清理并构建

mvn clean install

仔细看上面的构建指令:mvn install,而不是 mvn default,这是因为在 mvn 指令后面接的是生命周期中的某个阶段,接下来就详细解释下阶段的含义。

2.1.生命周期的阶段

每个生命周期是由一个一个阶段组成的,我们先从一张官网的截图来感性的认知一下阶段:

上图就是 Maven 生命周期中的完整的阶段,第一次看到这个图的话,可能会看的有点眼花缭乱,但是没有关系,我们暂时先不管这些阶段在做什么,先对这些步骤做一下拆解。

在运行的时候会按照上图中从上到下的顺序运行,直到运行到指定的阶段为止,例如上面的 mvn clean install 指令,会按下面的步骤运行:

找到clean生命周期,从pre-clean开始往下执行,直到clean阶段执行完成。找到default生命周期,从validate开始往下执行,直到install阶段执行完成。

这里的没有指定 site 直接忽略,clean和 default 大概会执行20多个阶段,看起来还是很多,但实际上这20多个阶段中有很大一部分是不会运行的,这涉及到一个特性:没有绑定插件目标的阶段,在生命周期中不会被调用(插件目标的概念可以看下面的标题2.2)。

If a build phase has no goals bound to it, that build phase will not execute.

这就好办了,我们只需要关注绑定了插件目标的阶段就好了,Maven 针对不同的打包格式,提供了不同的默认插件目标绑定规则,以打包成jar来举例:

上图就是我们日常工作中常用的阶段,相比上面那张图就简单多了,接下来一一解释一下这些阶段的含义:

clean:默认是清除target目录中的所有文件,避免将历史版本打到新的包中造成一些不在预期中的问题。process-resources:将资源文件src/main/resources下的文件复制到target/classes目录中。compile:将src/main/java下的代码编译成 class 文件,也放到target/classes目录中。process-test-resources:将资源文件src/test/resources下的文件复制到target/test-classes目录中。test-compile:将src/test/java下的代码编译成 class 文件,也放到target/test-classes目录中。test:运行单元测试并在target/surefire-reports中生成测试报告。package:将资源文件、class文件、pom文件打包成一个jar包。install:将生成的jar包推送到本地仓库中。deploy:将生成的jar包推送到远程仓库中。

现在我们执行一次deploy指令,看看控制台中打印出来的结果,对比一下是不是这些插件目标,篇幅问题,这里只放demo-a部分的结果,用红色字体标出的插件目标:

[INFO] — maven-clean-plugin:3.1.0:clean (default-clean) @ demo-a — [INFO] Deleting E:\workspace\code\dependency-demo\demo-a\target [INFO] [INFO] — maven-resources-plugin:3.2.0:resources (default-resources) @ demo-a — [INFO] Using ‘UTF-8’ encoding to copy filtered resources. [INFO] Using ‘UTF-8’ encoding to copy filtered properties files. [INFO] Copying 1 resource [INFO] Copying 0 resource [INFO] [INFO] — maven-compiler-plugin:3.8.1:compile (default-compile) @ demo-a — [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to E:\workspace\code\dependency-demo\demo-a\target\classes [INFO] [INFO] — maven-resources-plugin:3.2.0:testResources (default-testResources) @ demo-a — [INFO] Using ‘UTF-8’ encoding to copy filtered resources. [INFO] Using ‘UTF-8’ encoding to copy filtered properties files. [INFO] Copying 1 resource [INFO] [INFO] — maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ demo-a — [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to E:\workspace\code\dependency-demo\demo-a\target\test-classes [INFO] [INFO] — maven-surefire-plugin:2.22.2:test (default-test) @ demo-a — [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.ls.maven.AppTest [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.019 s - in com.ls.maven.AppTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] — maven-jar-plugin:3.2.2:jar (default-jar) @ demo-a — [INFO] Building jar: E:\workspace\code\dependency-demo\demo-a\target\demo-a-1.0.3.jar [INFO] [INFO] — maven-install-plugin:2.5.2:install (default-install) @ demo-a — [INFO] Installing E:\workspace\code\dependency-demo\demo-a\target\demo-a-1.0.3.jar to E:\workspace\repository\com\ls\maven\demo-a\1.0.3\demo-a-1.0.3.jar [INFO] Installing E:\workspace\code\dependency-demo\demo-a\pom.xml to E:\workspace\repository\com\ls\maven\demo-a\1.0.3\demo-a-1.0.3.pom [INFO] [INFO] — maven-deploy-plugin:2.8.2:deploy (default-deploy) @ demo-a — Uploading to nexus: http://192.168.200.101:8081/repository/maven-releases/com/ls/maven/demo-a/1.0.3/demo-a-1.0.3.jar Uploaded to nexus: http://192.168.200.101:8081/repository/maven-releases/com/ls/maven/demo-a/1.0.3/demo-a-1.0.3.jar (2.4 kB at 82 kB/s) Uploading to nexus: http://192.168.200.101:8081/repository/maven-releases/com/ls/maven/demo-a/1.0.3/demo-a-1.0.3.pom Uploaded to nexus: http://192.168.200.101:8081/repository/maven-releases/com/ls/maven/demo-a/1.0.3/demo-a-1.0.3.pom (876 B at 26 kB/s) Downloading from nexus: http://192.168.200.101:8081/repository/maven-releases/com/ls/maven/demo-a/maven-metadata.xml Downloaded from nexus: http://192.168.200.101:8081/repository/maven-releases/com/ls/maven/demo-a/maven-metadata.xml (298 B at 17 kB/s) Uploading to nexus: http://192.168.200.101:8081/repository/maven-releases/com/ls/maven/demo-a/maven-metadata.xml Uploaded to nexus: http://192.168.200.101:8081/repository/maven-releases/com/ls/maven/demo-a/maven-metadata.xml (329 B at 15 kB/s)

注:运行deploy指令需要先配置私服,可以参考 (三)Maven仓库概念及私服安装与使用 附:Nexus安装包下载地址,将下面的配置改成成自己的私服地址。

nexus

http://192.168.200.101:8081/repository/maven-releases/

nexus

http://192.168.200.101:8081/repository/maven-snapshots/

如果还想了解其他打包方式的插件目标绑定关系,可以到官网进行查阅。

2.2.插件目标

插件目标非常容易理解,其实就是在某个插件中的某个功能,例如:compiler:compile是属于 maven-compiler-plugin 里面的一个功能,compiler 是目标前缀(goalPrefix),compile就是目标(goal)。

两者组成了一个坐标,绑定到了compile这个阶段上,当运行到这个阶段的时候,就会通过这个坐标找到对应编译功能的代码并运行。

跳过测试插件目标 我们每次执行构建时,到走到生命周期中的test阶段,就会把项目中的单元测试全都运行一遍,单元测试较多的时候运行会比较耗时,在项目没有强制要求不需自动运行单元测试的情况下,我们可以通过下面的指令跳过测试阶段。

mvn clean install -D maven.test.skip=true

…… [INFO] — maven-resources-plugin:3.2.0:testResources (default-testResources) @ demo-a — [INFO] Not copying test resources [INFO] [INFO] — maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ demo-a — [INFO] Not compiling test sources [INFO] [INFO] — maven-surefire-plugin:2.22.2:test (default-test) @ demo-a — [INFO] Tests are skipped. ……

上面执行结果中,用红色标记的插件目标执行结果发生了变化,不再复制src/test下面的资源文件、也不再编译src/test目录下的代码,并且直接跳过了测试阶段。

2.3.其他插件绑定

除了Maven自带的默认插件以外,我们还可以引入官方或者三方编写的非默认的插件,与类似,在标签中通过坐标引入插件,通过执行计划

例如,我们有时候不仅需要将jar包推送到私服中,还需要将源码一并推送,方便使用者可以直接查看源码中的注释以及实现代码,这时候就需要引入源码插件。

2.3.1.源码生成插件

org.apache.maven.plugins

maven-source-plugin

3.0.1

install

build-source

jar-no-fork

这里在install这个阶段加入了生成源码的插件目标,也就是说在install阶段上有了两个插件目标,从执行结果来看,两个插件目标都被依次执行了。这涉及到绑定插件目标的另一个特性:同一个阶段中可以绑定多个插件目标,在这个阶段中,会按照POM中定义的顺序执行。

If a build phase has no goals bound to it, that build phase will not execute. But if it has one or more goals bound to it, it will execute all those goals. multiple goals bound to a phase are executed in the same order as they are declared in the POM.

2.3.2.SpringBoot打包插件

SpringBoot的jar包与普通的包对比起来主要有两点差别:

能够通过java -jar启动jar包里面除了项目中本身的文件以外,还把3方jar包也一起打包了

Maven 自带的maven-jar-plugin插件不具备这种能力,于是需要引入SpringBoot的打包工具。

org.springframework.boot

spring-boot-maven-plugin

运行 mvn clean package 查看运行结果,可以看到执行了repackage这个插件目标:

这里有个疑问,为什么这个插件不需要指定以及呢?

这是因为在pom.xml中继承了spring-boot-starter-parent,在这个pom中定义了,所以可以直接使用,就类似于

org.springframework.boot

spring-boot-starter-parent

2.6.7

再看一下父级pom中的定义:

这里没有指定phase,插件目标是怎么绑定到package阶段的呢?

这是因为在编写插件的时候,在插件的配置文件中就已经指定了repackage这个插件目标绑定在package这个阶段上了,在spring-boot-maven-plugin-2.6.7.jar 这个包中,我们可以找到一个plugin.xml的配置文件,打开后可以查看到如下的内容:

如果你对自己编写一个 Maven 插件没有什么需求的话,了解到这里就已经足够了,这个配置文件在后续的自定义 Maven 插件中还会更详细的讲解。

3.总结

本篇主要讲解的是Maven 通过生命周期阶段与插件目标的绑定来实现项目的构建流程,主要涉及到以下的知识点:

生命周期有三种,分别是clean,default,site每种生命周期都由多个阶段组成执行构建时,会按照阶段顺序从上到下的执行,但只有绑定了插件目标的阶段才会执行可以在通过标签引入插件,通过来定义执行计划,通过绑定阶段与插件模板除了在pom.xml中指定阶段与插件目标的绑定关系之外,还可以通过插件的配置文件plugin.xml进行指定

下面是一张以打包成jar为例的执行阶段图,我们大部分的项目都会按照下图的顺序进行处理。

参考文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。