1. 生命周期与插件机制
- 由于使用了高度集成的IDE,我们对一个Java项目的构建过程,能直观接触的有编译、测试、打包、部署等步骤。
- 这些步骤,要么是手工操作,要么通过脚本实现。
- 例如,使用脚本实现项目的构建,换一个项目,脚本一般都需要重新编写,可移植性很差。
- 针对这样的情况,maven提出了生命周期和插件机制。
1.1 概述
- 生命周期是对项目构建的各个步骤的抽象。同时,它还定义了构建步骤之间的次序。例如,测试之前必须先编译。
- 生命周期本身不做任何的实际工作,实际的工作都是由插件完成。例如,代码的编译有对应的编译插件。
- 每个构建步骤可以绑定一个或多个插件,maven为大多数构建步骤绑定了默认插件,也支持用户为构建步骤配置插件,甚至可以编写插件。
1.2 生命周期和插件机制带来的好处
- 绑定默认的插件,使得maven项目有一致的构建标准。
- 同时,默认插件还简化了实际项目的构建。因为,用户不需要学习如何绑定插件,使用默认插件即可。
- 支持配置或编写插件,扩展了maven项目构建的空间。
2. 三套生命周期
- maven拥有三套相互独立的生命周期:
clean
、default
、site
,分别对应项目的清理、真正构建时的所有步骤(如编译、测试、部署)、项目站点的生成。 - 每个生命周期都包含多个阶段(phase),这些阶段是前后依赖的。
- 例如,生命周期
clean
,包含pre-clean
、clean
、post-clean
三个阶段,执行clean
阶段,会先执行pre-clean
,而非只执行clean
。
2.1 clean生命周期
pre-clean
:执行一些清理前需要完成的工作clean
: 清理上一次构建生成的文件post-clean
:执行一些清理后需要完成的工作
2.2 default生命周期
-
内容比较多,挑重点说:顺序大致为处理主资源、编译主代码、处理测试资源、编译测试代码、测试、打包、安装和部署
-
其中,
process-resources
:将src/main/resources
目录内的内容进行变量替换等工作后,复制到项目输出的主classpath目录(target/classes
)中。这解释了为啥,我通过如下代码获取yaml文件的路径时,发现是
target/classes
,而非src/main/resources
。明明我的yaml文件是放在src/main/resources
目录的
书中说,这是process-sources
,顾名思义和查资料,我发现书中说的不对 😂String path = YamlUtils.class.getClassLoader().getResource("xx.yaml").getPath()
-
compile
:编译主代码,即编译src/main/java
目录下的代码,并输出到主classpath目录(target/classes
)中。 -
对应的,
process-test-resources
的输出目录变成了target/test-classes
、test-compile
的输出目录变成了target/test-classes
-
剩下的就是
test
、package
、install
2.3 site生命周期
- 包含
pre-site
、site
、post-site
、site-deploy
三个节点
2.4 生命周期与maven命令
mvn clean
对应clean生命周期的clean阶段mvn test
对应default生命周期的test阶段mvn clean install
对应clean生命周期的clean阶段和default生命周期的install阶段- 从上面的例子可以看出,常用的maven命令都是基于生命周期的某些阶段简单组合而成
3. 插件目标与插件绑定
3.1 插件目标
- 为了复用插件,一个插件往往能完成多个任务。也就是说一个插件往往具有多个功能。
- 例如,之前用过
mvn dependency:xxx
去获取、分析项目的依赖,这些功能都集中于一个插件maven-dependency-plugin
。 - 我们将插件中的每个功能称为插件目标,通过插件名关键字:插件目标,便可以在maven命令中使用插件目标。这是插件目标的通用写法。
- 例如,
maven-compiler-plugin
的插件名关键字为compiler
,其包含一个插件目标compile
。因此,该插件目标的通用写法为compiler:compile
3.2 插件绑定
- maven的生命周期与插件相互绑定,实质上是生命周期阶段与插件目标的相互绑定。
- 例如,
maven-compiler-plugin
插件的compile
功能与default
生命周期的compile
阶段相互绑定,通过调用插件的compile
功能就能完成代码的编译。
3.3 自定义插件绑定
-
default
生命周期中的有一个叫做verify
的阶段,通过如下配置可以将maven-source-plugin
的jar-no-fork
功能绑定,从而可以创建项目的源码jar包。<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>3.0.1</version> <executions> <!-- 通过execution子元素为生命周期阶段配置插件目标--> <execution> <!-- 任务名,可以理解成是绑定关系的id--> <id>verify-bind</id> <!-- 生命周期阶段--> <phase>verify</phase> <goals> <!-- 插件目标--> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin>
-
执行
mvn verify
命令,可以触发default生命周期的verify阶段,该阶段完成的实际任务是将项目源代码打包。-
maven-source-plugin:3.0.1:jar-no-fork
是插件和插件目标 -
verify-bind
是插件绑定关系的id,书上数这是任务id。 -
通过插件绑定,生成了项目源码的jar包:
jianzhioffer-1.0-SNAPSHOT-sources.jar
[INFO] --- maven-source-plugin:3.0.1:jar-no-fork (verify-bind) @ jianzhioffer --- [INFO] Building jar: /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT-sources.jar
-
3.4 maven的默认插件绑定
-
为了降低用户的使用门槛,maven为一些主要的生命周期阶段绑定了很多插件目标,只需要简单的执行maven命令,就可以完成一些构建任务。
-
clean生命周期的clean阶段与
maven-clean-plugin
的clean
插件目标默认绑定 -
default生命周期的默认绑定如下表所示:
生命周期阶段 插件 插件目标 执行的任务 process-resources maven-resources-plugin resources 复制主资源文件到主输出目录 compile maven-compiler-plugin compile 编译主代码至主输出目录 process-test-resources maven-resources-plugin testResources 复制测试资源文件到测试输出目录 test-compile maven-compiler-plugin testCompile 编译测试代码至测试输出目录 test maven-surefire-plugin test 执行单元测试 package maven-jar-plugin jar 创建项目jar包 install maven-install-plugin install 将项目构件安装到本地仓库 deploy maven-deloy-plugin deploy 将项目构件部署到远程仓库 -
site生命周期的site阶段与
maven-site-plugin
的site
插件目标默认绑定;site-deploy阶段与maven-site-plugin
的deploy
插件目标默认绑定 -
通过执行
mvn clean install
命令,我们可以看到clean阶段对应的默认插件目标,以及defaul生命周期到install阶段对应的所有默认插件目标。# clean阶段 [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ jianzhioffer --- [INFO] Deleting /Users/xxxx/IdeaProjects/JianzhiOffer/target # process-resources阶段 [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jianzhioffer --- [INFO] Copying 0 resource # compile阶段 [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ jianzhioffer --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to /Users/xxxx/IdeaProjects/JianzhiOffer/target/classes # process-test-resources阶段 [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ jianzhioffer --- [INFO] skip non existing resourceDirectory /Users/xxxx/IdeaProjects/JianzhiOffer/src/test/resources # test-compile阶段 [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ jianzhioffer --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to /Users/xxxx/IdeaProjects/JianzhiOffer/target/test-classes # test阶段 [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ jianzhioffer --- [INFO] Surefire report directory: /Users/xxxx/IdeaProjects/JianzhiOffer/target/surefire-reports ... # package阶段 [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ jianzhioffer --- [INFO] Building jar: /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT.jar # install阶段 [INFO] --- maven-install-plugin:2.4:install (default-install) @ jianzhioffer --- [INFO] Installing /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT.jar to /Users/xxxx/repo/org/example/jianzhioffer/1.0-SNAPSHOT/jianzhioffer-1.0-SNAPSHOT.jar [INFO] Installing /Users/xxxx/IdeaProjects/JianzhiOffer/pom.xml to /Users/xxxx/repo/org/example/jianzhioffer/1.0-SNAPSHOT/jianzhioffer-1.0-SNAPSHOT.pom
3.5 查看插件信息
-
maven中的插件在编写时,一般都绑定了默认的生命周期阶段。例如,上面自定义
verify
阶段的插件目标时,其实去除<phase>
标签,仍然可以看到maven-source-plugin:3.0.1:jar-no-fork
的输出。 -
可以通过如下命令查看插件的信息,包括插件中的所有插件目标、每个插件目标默认绑定的生命周期阶段等。
# 命令 mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:3.0.1 -Ddetail
-
部分输出信息:
# 输出的部分信息 source:jar-no-fork Description: This goal bundles all the sources into a jar archive. This goal functions the same as the jar goal but does not fork the build and is suitable for attaching to the build lifecycle. Implementation: org.apache.maven.plugins.source.SourceJarNoForkMojo Language: java Bound to phase: package
3.6 自己对插件绑定的分析
基于jar-no-fork
插件目标进行分析:
-
使用默认绑定:
jar-no-fork
原本的默认绑定是package
阶段,不指定<phase>
标签,默认与package
阶段绑定。- 执行
mvn verify
时,根据阶段之间的前后依赖关系,会先执行package
阶段。因此,会有maven-source-plugin:3.0.1:jar-no-fork
的输出。
-
指定生命周期阶段:
- 如果明确指定
<phase>
标签,则将与verify
阶段绑定。 - 执行
mvn verify
至verify
阶段时,会有maven-source-plugin:3.0.1:jar-no-fork
的输出。
- 如果明确指定
-
同一插件目标,绑定多个阶段: 按照阶段的先后顺序,多次执行目标插件
-
绑定信息如下:
<executions> <execution> <id>verify-bind</id> <phase>verify</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> <execution> <id>package-bind</id> <phase>package</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions>
-
执行输出如下:
[INFO] --- maven-source-plugin:3.0.1:jar-no-fork (package-bind) @ jianzhioffer --- [INFO] Building jar: /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT-sources.jar [INFO] [INFO] --- maven-source-plugin:3.0.1:jar-no-fork (verify-bind) @ jianzhioffer --- [INFO] Building jar: /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT-sources.jar
-
-
同一阶段,绑定多个插件目标: 按照插件定义的先后顺序进行执行
-
绑定关系如下:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.1.1</version> <executions> <execution> <id>package-shade</id> <phase>package</phase> <goals> <goal>shade</goal> </goals> ... </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>package-source-jar</id> <phase>package</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin>
-
输出信息如下:
# 执行package阶段的默认绑定 [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ jianzhioffer --- [INFO] Building jar: /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT.jar # 执行package阶段的自定义绑定:package-shade [INFO] --- maven-shade-plugin:3.1.1:shade (package-shade) @ jianzhioffer --- [INFO] Replacing original artifact with shaded artifact. [INFO] Replacing /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT.jar with /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT-shaded.jar # 执行package阶段的自定义绑定:package-source-jar [INFO] --- maven-source-plugin:3.0.1:jar-no-fork (package-source-jar) @ jianzhioffer --- [INFO] Building jar: /Users/xxxx/IdeaProjects/JianzhiOffer/target/jianzhioffer-1.0-SNAPSHOT-sources.jar
-