生命周期详解:
Maven的生命周期就是对
项目构建过程进行的抽象和统一,就是项目构建的流程。但是构建过程中每一步(例如编译源代码)的
实际行为都由插件来完成的。
Maven的生命周期不是一个整体,它
拥有三套相互独立的生命周期(clean,default,site)而且
每套生命周期下面包含一些阶段,
这些阶段是有顺序的,并且
同一个生命周期内后面的阶段依赖前面阶段的完成,也就是说当我们调用生命周期内的某个阶段C时,该生命周期内阶段C之前的阶段也会自动执行,
用户对Maven的操作其实就是
调用这些生命周期下的的一个或者多个阶段。以clean生命周期为例,它包含三个阶段:pre-clean,clean,post-clean。当调用clean时,pre-clean,clean阶段会得以顺序执行。
- clean生命周期:清理项目,下面是它包含的三个阶段
- pre-clean:执行一些清理前必须完成的工作
- clean:清理上一次构建生成的文件
- post-clean:执行一些清理后需要完成的工作
- default生命周期:构建项目,下面是它包含的几个重要的阶段
- validate:验证工程是否正确,所有需要的资源是否可用。
- process-resources:复制和处理资源文件到target目录,准备打包。
- compile:编译项目的源代码。
- test-compile:编译测试源代码;
- test:运行测试代码;
- package:打包成jar或者war或者其他格式的分发包;
- integration-test:如有需要,将包处理和发布到一个能够进行集成测试的环境。
- verify:运行所有检查,验证包是否有效且达到质量标准。
- install:将打好的包安装到本地仓库,供其他项目使用;
- deploy :将打好的包安装到远程仓库,供其他项目使用;
- site生命周期:建立项目站点,下面是包含的四个阶段
- pre-site:生成项目站点之前需要完成的工作
- site:生成项目的站点文档;
- post-site:生成项目站点之后需要完成的工作
- site-deploy:将项目站点发布到服务器
较之于
生命周期阶段的前后依赖关系,三套
生命周期本身是相互独立的。也就是说:
用户可以仅调用某生命周期的某个阶段,而不会触发其他生命周期或者对其他生命周期有什么影响。例如:用户调用default生命周期的compile阶段,不会触发clean生命周期的任何阶段。
从命令行执行Maven任务的最主要的方式就是调用Maven的生命周期阶段。下面以一个常见的Maven命令举例:
$mvn clean install site-deploy: 该命令调用clean生命周期的clean阶段,default生命周期的deploy阶段,以及site生命周期的site-deploy阶段。实际执行的是clean生命周期的pre-clean和clean阶段,default生命周期的install以及其之前的所有阶段,site生命周期的全部阶段。
插件目标:
前面我们已经知道,Maven核心仅仅定义了抽象的生命周期,具体的任务是交由插件完成的。假设我们有三个插件,需要完成的功能分别是:分析项目依赖,列出项目依赖树,列出项目所有已经解析的依赖。因为功能的相似,我们发现这三个插件之间有很多可以复用的代码,所以为每一个功能都写一个独立的插件是不可取的,
于是我们可以把这三个功能聚集在一个插件里,每个功能作为一个插件目标。例如:maven-dependency-plugin插件有十多个目标,每个目标对应了一个功能:dependency:analyze,dependency:tree,dependency:list。
这是一种通用的写法,冒号前面是插件插件前缀,冒号后面是该插件的目标。
插件绑定:
具体来说,生命周期的阶段与插件互相绑定,以完成某个具体的构建任务。其中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-surfire-plugin:test
- package ->> maven-jar-plugin:jar
- install ->> maven-install-plugin:install
- deploy ->> maven-deploy-plugin:deploy
- site生命周期:建立项目站点,下面是包含的四个阶段
- site ->> maven-site-plugin:site
- site-deploy ->> maven-site-plugin:deploy
自定义绑定:
自定义绑定允许我们将某个插件目标绑定到生命周期的某个阶段上,下面以生成项目主代码的源码jar为例。使用到的插件和他的目标为:maven-source-plugin:jar-no-fork、将其绑定到default生命周期阶段verify上(可以任意指定三套生命周期的任意阶段)、在项目的POM配置中(也可以在父POM中配置、后面聚合与继承会有提到)。
上述配置有插件的坐标声明,
excutions下面每个excution子元素配置的执行的一个任务,
通过phase指定与生命周期的那个阶段绑定、在通过goals指定执行绑定插件的哪些目标。当我们执行verify生命周期阶段时,会自动调用
maven-source-plugin:
2.1.1:
jar-no-fork(
attach-sources
)
当插件的目标绑定到不同的生命周期阶段的时候,插件目标的执行顺序是有生命周期阶段的顺序决定的,当多个插件目标绑定到同一生命周期阶段的时候,顺序是按照插件声明的顺序来决定目标的执行顺序。
命令行插件配置:
因为通过命令行插件配置,可以
灵活得改变插件的行为,使其更符合需要。
使用语法:
maven命令 -D参数=参数值
例如:maven-surfier-plugin提供了一个maven.test.skip参数,当它为true时,它就会跳过测试。我们运行如下命令:
$ mvn install -Dmaven.test.skip = true
maven-surfier-plugin插件是绑定在test阶段的插件,是在install阶段之前被执行的。
POM中插件全局配置:
对于一些
参数值从项目创建到发布都
不会或者很少会改变的情况,在POM文件中一次性配置显然比每次在命令行输入方便。
用户可以在
声明插件的时候,
对插件进行一个全局的配置<configuration>,也就是说所有
基于该插件的目标,都会使用这些配置。例如:我们通常会需要配置
maven-compile-plugin告诉它编译Java 1.7版本的源代码,生成与JVM 1.7兼容的字节码文件,这样不管绑定到
compile阶段的maven-compile-plugin:compile任务,还是绑定的到
test-compile阶段的maven-compile-plugin:testCompile任务,
只要使用了该插件,就都能使用该全局配置,基于Java1.7版本进行编译。
POM中插件任务配置:
除了为插件配置全局变量,我们还可以
为某个插件任务配置特定的参数。
以maven-antrun-plugin为例,它有一个目标run,可以用来在Maven中调用Ant任务。用户将maven-antrun-plugin:run 绑定到多个生命周期阶段上,再加以不同的配置,就可以让Maven在不同的生命阶段执行不同的任务,如:
在上述代码片段中,首先,maven-antrun-plugin:run 与 validate阶段绑定,从而构成一个id为ant-validate的任务。
插件全局配置中得configuration元素位于plugin元素下面,而这里的configuraction元素则位于execution元素下,表示这是特定任务的配置,而非插件整体的配置。
这个ant-validate任务配置了一个echo Ant任务,向命令行输出一段文字,表示该任务是绑定到validate阶段的。
第二个任务的id为ant-verify,它绑定到了verify阶段,同样它也输出了一段文字到命令行,告诉该任务绑定到了verify阶段。
插件解析机制:
插件仓库:插件同样基于坐标存储于Maven仓库中,但是Maven区别对待依赖的远程仓库与插件的远程仓库,插件的远程仓库使用pluginRepositories和pluginRepository配置,其他的子元素和依赖远程仓库的配置一样。
在POM中配置插件的时候,如果插件是Maven的官方插件(即如果其groupie为org.apache.maven.plugins),可以省略groupId的配置。
我们也可以配置自己默认的groupId,在Maven的settings.xml中添加如下内容。
Maven在超级POM中为所有核心插件(maven-clean-plugin,maven-compiler-plugin,maven-surefire-plugin等)设定了版本,当用户使用某个插件没有设定版本而且又不是核心插件时,Maven会合并这个插件在本地仓库和远程仓库的元数据maven-metadata.xml,计算出最新的release版本并使用,但是这存在风险,所以应该显示声明插件版本。
解析插件前缀:插件前缀与groupId:artifactId是一一对应的,这种匹配关系存储在仓库元数据中,但与之前
解析版本所用到的groupId/artifactId/maven-metadata.xml不同,这里的
仓库元数据为groupId/maven-metadata.xml(该groupId下所有插件的),且Maven在解析插件仓库元数据的时候,会默认使用org.apache.maven.plugins和org.codehaus.mojo这两个groupId,也可以通过pluginGroup的配置让Maven检查其他groupId上的插件仓库元数据。
最后用一个例子来总结一下插件的解析机制:
mvn compiler:compiler 是怎么解析为一个完整路径的
1. 先补上被省略的默认的
groupId(也可以由pluginGruop自己配置)—> mvn org.apache.maven.plugins:compile:compile
2.去maven的默认的插件远程仓库下比对元数据,
Maven默认的远程仓库是
http://repo.maven.apache.org/maven2/
,所有插件元数路径则是:
http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml
,我们找到compiler插件的元数据,如图:
这里会根据prefix指定的前缀找到对应的artifactId. — >
mvn org.apache.maven.plugins:
maven-compiler-plugin
:compile
3.
我们再根据groupId和artifactId找到maven-compiler-plugin插件单个的元数据,
路径为
http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml
,如图:
maven将所有的远程插件仓库及本地仓库元数据归并后,就能找到release的版本,最后命令被拓展成了这样:
mvn org.apache.maven.plugins:maven-compiler-plugin:3.0.0:compiler。