该文章为maven系列学习的第三篇,也是最后一篇
第一篇快速入口:从0到0.1学习 maven(一:概述及简单入门)
第二篇快速入口:从0到0.1学习 maven(二:坐标、依赖和仓库)
构建步骤包括项目清理,初始化,编译,测试,打包,集成测试,验证,部署等等。maven将这些过程进行了抽象与统一,映射到了生命周期上。
可以将maven的生命周期理解成设计模式中的模板方法。父类定义整体结构,子类进行具体方法的实现和重写。在控制整体结构的同时也增加了可拓展性。maven没有对这些步骤提供实现,但是提供了默认绑定的插件,因此实际上每个步骤都是通过插件完成的。
实际上,maven中的生命周期分成了三套,分别为clean,default与site,分别对应着 清理,构建与建立项目站点。每套生命周期都对应着一些有序的阶段。等下会详细介绍。三套生命周期本身是相互独立的,也就是调用clean不会影响到site。
阶段1:pre-clean: 执行清理前需要完成的工作
阶段2:clean:清理上一次构建生成的文件
阶段3:post-clean:执行一些清理后需要完成的工作
阶段间是有序的,因此若想调用post-clean,会按preclean-clean-postclean的顺序执行。
在default周期中,定义了真正构建时需要的步骤。官方介绍可以参考:https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
该周期的主要作用为建立和发布项目站点。根据pom中包含的信息,自动生成站点。
首先介绍一下插件目标,可以将一个小功能点理解成一个目标。前面提到核心功能都是通过插件的形式来实现的,但是针对一个功能点就开发一个插件显然会有很多冗余代码。因此一个插件可能会包含多个插件目标。
生命周期会与插件相互绑定,例如对于A过程, 插件B可以完成该任务,因此将AB进行绑定。如果某过程没有绑定插件,那该过程就不会有实际行为。
为了让用户几乎不用配置就可以构建maven项目,maven会对主要的生命周期阶段进行内置的插件绑定。
另外,一个插件也可以绑定多个阶段,例如clean生命周期的三个阶段都绑定在了maven-clean-plugin:clean插件上。
除了内置的绑定之外,用户也可以对阶段使用的插件进行自定义的绑定。
举一个自定义绑定插件的例子
org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources verify jar
可以看到,在execution元素下,指定了phase(阶段)和goal(目标)。
尝试删除phase阶段,再次执行mvn verfify,执行依然成功。因为很多插件的目标在编写的时候已经默认绑定了阶段。可以通过mvn help:describe -Dplugin=xxxxx -Ddetail
来查看具体信息
运行mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:2.2.1 -Ddetail
看一下我们刚刚用的包的细节。
第五行:Bound to phase: package 指明了他绑定的默认生命周期阶段时package。
ps: 如果不加版本号,则自动获取最新的版本。加了detail是为了更详细的信息。如果仅仅想知道某个目标的信息,可以使用goal参数。
命令行配置
maven沿用了java的命令行传参模式即“-D”
例如上述例子中 -Dplugin=xxx就表示着传递 名为plugin,值为xxx的参数。
POM中插件全局配置
对于很少或不会改变的参数,直接写死在pom文件中。
直接在version下面加一个configuration的子标签
1.5 1.5
POM中配置任务参数
还可以为某个插件任务配置特定的参数
I am a task
首先,需要了解一个插件前缀的概念。我们刚刚使用了mvn help:describe"
实际上是maven-help-plugin插件的describe目标。这里help就是插件前缀,其结构是groupId:artifactId。复习一下,依赖仓库的路径groupId/artifactId/maven-metadata.xml,插件仓库的元数据是在groupId/maven-metadata.xml下的。
插件像构件一样,以坐标的形式存储在maven仓库中。
插件仓库的子元素配置和依赖仓库完全相同。
central Central Repository https://repo.maven.apache.org/maven2 default false
如果是maven的官方插件(groupId=org.apache.maven.plugins),则可以省略groupId配置。
对于核心插件,maven为它们在超级pom(所有maven项目的父pom)中预先设定了版本。因此核心插件可以不用指定版本。
对于非核心插件,类似于仓库的版本解析,插件也有元数据文件。maven遍历本地与远程插件仓库,找到lastest和release。maven2会使用latest,maven3会使用release。
实际项目中,可能是会由多个项目组合起来的,需要对每个项目进行打包构建。如果每次都一个一个地去模块对应的目录下构建那是相当麻烦。因此聚合,也叫多模块 就是在一个目录可以构建项目中的所有包。
首先需要创建出一个额外的模块,并写pom文件
4.0.0 com.company.sc demo 1.0.0-SNAPSHOT pom a_practise_demo 用于练习聚合的项目 module_name_1 module_name_2 module-name-3
它与普通的模块有三处不同
在maven世界里也有继承这个概念。像聚合一样,需要单独抽出一个目录,编写父pom以完成“一处声明,多处使用”的目的。
4.0.0 com.company.sc demo-parent 1.0.0-SNAPSHOT pom a_practise_demo
它与聚合有两点相同:
继承父类的子模块需要加入如下内容
com.company.sc demo-parent 1.0.0-SNAPSHOT ../dir1/dir2/pom.xml
groupId, artifactId, version 是定义坐标系的三个最基本的元素,是必须的。relativePath表示父pom的相对路径,默认值是…/pom.xml,也就是maven默认父pom在上一层目录下。
另外,如果有聚合模块的话,该继承模块也需要被添加到modules标签下。
父类的依赖可以被子类继承,但不一定所有的父类依赖都会被每个子类使用。这里maven提供了dependencyManagement元素,能让子模块继承到父模块的依赖配置,又能保证灵活性。因为dm元素下的依赖声明不会引入实际的依赖,但却能控制依赖。比如可以在父pom中声明依赖的groupId, artifactId, version,scope,子模块中就只需要groupId, artifactId,其他都可以从父类中继承。
依赖范围import只在dependencyManagement中才有效果。它的作用是将某pom中dm标签下的配置导入并合并到当前pom的dm元素中。假设另外一个模块想要导入我们前面的父类pom,可以通过以下配置。
com.company.sc demo-parent 1.0.0-SNAPSHOT pom import
一般import都会指向打包类型为pom的模块,因此type元素的值为pom。
类似于刚刚的依赖管理,插件相应地也可以使用pluginManagement进行管理:在父pom中配置plugin元素,当子pom中继承并配置了对应地groupId和artifactId,pm中的配置才会生效。
对于聚合模块,它知道有哪些被聚合的模块,但是被聚合的模块不知道它。对于继承模块,继承该模块的子模块知道他,但是他不知道都有谁继承了他。两个关系刚好相反,但是配置的时候packaging都必须是pom。在实际项目中,一个pom可能又当聚合pom又当父pom。
反应堆指的是所有的模块形成的一个构建结构。拿以下模块为例子,该部分从demo的pom中提取。
module_name_child_1 module_name_father module_name_no_father
反应堆的构建顺序与声明顺序不一定一致,这个要取决于聚合与依赖关系。实际的构建顺序应为
demo->module_name_father->module_name_child_1->module_name_no_father
demo是聚合模块的起点,肯定是第一个构建的,然后按照顺序读取到了module_name_child_1,检查到它依赖着module_name_father,于是先去构建module_name_father。构建好了之后再构建module_name_father。接着继续按顺序,构建到module_name_no_father。一般来讲,依赖关系会将反应堆构建为有向无环图,如果有环,在构建时maven就会报错。
如果想构建反应堆中的某些模块,可以使用mvn -h
举个例子,假设a依赖于b和c,如果用-am a的话,就会同时构建a b c。如果用-amd b的话,就会构建 a b。假设之前的构建顺序是 a b c d,那-rf c 就会构建c和d