Maven的
聚合特性能够把项目的各个模块聚合在一起构建,而Maven的
继承特性则能帮助抽取各模块间相同的依赖和插件配置,还能促进各个模块之间配置的一致性。
聚合:
我们在开发过程中,将项目拆分成独立的子模块,每个模块都是一个独立的maven project,在开始的时候我们可以独立的编译和测试运行每个模块,但是我们期望能够使用简单的操作来完成所有项目的编译等工作,这时Maven给出了聚合的配置方式。我们可以建立一个专门负责聚合工作的Maven project — aggregator,然后通过该模块构建整个项目的所有模块。
该聚合项目特征以及约定:
- 该aggregator本身也做为一个Maven项目,它必须有自己的POM
- 约定:为了方便构建,通常将聚合模块放在项目目录层的最顶层,其它子模块作为其子目录存在。这样当我们打开项目的时候,第一个看到的就是聚合模块的POM。
- 聚合模块的版本和被聚合模块版本需一致
- 它的打包方式必须为:pom,即pom
- 引入了新的元素:及其下的,用户可以在一个打包方式为POM的Maven项目下声明任意数量的module元素来实现模块的聚合。我们声明child-project-A,那么聚合项目的POM就会默认去./child-project-A的目录下去寻找子模块,所以在module中声明的子模块应与聚合项目的POM处于同一目录下。如果我们不遵循约定,将子模块放于跟聚合模块同一个目录下,则modules的声明需要改成../child-project-A.
- 聚合模块的内容仅仅是一个pom.xml文件,因为它只是用来帮助其它模块构建的工具,本身并没有实质的内容,所以它不包含src/main/java、src/test/java等目录.
继承:
我们在项目开发的过程中,可能多个模块独立开发,但是多个模块可能依赖相同的元素或者需要引入相同的插件,比如说每个模块都需要Junit,使用spring的时候,其核心jar也必须都被引入,在编译的时候,maven-compiler-plugin插件也要被引入,这在每一个子模块里都是需要配置的,但是通过POM继承,我们可以抽取出重复的配置,精简代码。
我们可以建立一个专门负责继承工作的父模块 — parent,他有以下特征:
- 该parent本身也做为一个Maven项目,它必须有自己的POM
- 由于它只是为了消除配置的重复,所以继承模块的内容仅仅是一个pom.xml文件,本身并没有实质的内容
- 它的打包方式必须为:pom,即pom
继承他的子模块有以下特征:
- 在继承他的子模块的pom文件中,我们用元素来声明父模块,元素下的子元素groupId,artifactId,version指定了父模块的坐标,元素指定了父模块pom文件的路径(相对于该子模块的pom),
其默认值为
../,相当于去上一层目录找父pom。但是有时候会有问题,例如我们只从仓库中迁出了一个子模块,此时
元素多半就失效了,子模块只能根据父模块的坐标再去仓库中寻找。
- 子模块必须显示声明artifactId,他隐式得从父模块继承了groupId和version两个元素,只要当子模块与父模块的这两个元素不一致时,才需要显式声明他们。
可继承的POM元素:
- groupId:项目组ID,项目坐标的核心元素
- version:项目版本, 项目坐标的核心元素
- dependencies:项目的依赖配置
- repositories:项目的仓库配置
- distributionManagement:项目的部署配置
- dependencyManagement:项目的依赖管理配置
- pluginManagement:项目的插件管理配置
- build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等
- properties:自定义的maven属性
- description: 项目的描述信息
- organization: 项目的组织信息
- inceptionYear: 项目的创始年份
- url: 项目的URL地址
- developers: 项目开发者信息
- contributors: 项目的贡献者信息
- issueManagement: 项目的缺陷跟踪系统信息
- ciManagement: 项目的持续集成系统信息
- scm: 项目的版本控制系统信息
- mailingLists: 项目的邮件列表信息
- reporting: 包括项目的报告输出目录配置、报告插件配置等
依赖管理:
由上我们可知
dependencies是可以被继承的,我们就想到把几个子模块
共同依赖的元素A转移到parent中,这样我们又进一步的优化了配置。可是问题也随之而来,如果有一天又创建了一个继承parent的子模块,但是这个模块不需要依赖元素A,n那这时候如何处理?
在父模块的POM中
dependencyManagement元素帮我们解决了这个问题。我们在
dependencyManagement元素下
配置
依赖(同样是使用dependencies,dependency元素),但是这里(
父模块的POM中)声明的依赖不
会给父模块引入依赖,
也不会给子模块引入依赖,这段配置会被继承,子模块只有在POM中显式得声明依赖的groupId与artifactId,才能得到我们从父模块中继承而来的完整的依赖配置(例如包含了版本号,依赖范围等),如果不声明,即使依赖已经在父模块的
dependencyManagement中被声明了,但是也不会有什么实际的效果。
通过这样的配置,我们可以
统一项目范围中依赖的版本,降低各个子模块间由于依赖版本不一样可能引起的冲突。
更进一步,如果我们有多个父模块,那又该怎么抽取出各个父模块POM文件中重复的配置呢?我们只要在各个父模块POM文件中引入这些配置就好了。这里使用了一个新
的依赖范围:import,并且设置这个依赖的type为pom,代码如下:
插件管理:
类似于管理依赖的
dependencyManagement,Maven提供了
pluginManagement来管理插件,他的机制和
dependencyManagement一样,当子模块需要不同的插件配置,可以自行配置以覆盖父模块的pluginManagement配置。
...
maven-assembly-plugin
2.2-beta-2
create-project-bundle
package
single
project
...
继承和聚合总结:
对于聚合模块来说,它知道有哪些被聚合的模块,而对于被聚合的模块来说,它们不知道被谁聚合了,也不知道它的存在
对于继承关系的父POM来说,它不知道自己被哪些子模块继承了,对于子POM来说,它必须知道自己的父POM是谁
在一些实践中我们会发现:一个POM既是聚合POM,又是父POM,这么做主要是为了方便。
约定优于配置:
这是Maven最核心的设计理念之一,他大大简化了我们需要手动配置的内容,我们举个例子:Maven中
约定了源码目录为src/main/java/,编译输出目录为target/classes/,有了这个约定,我们就不再需要手动创建这些目录,Maven会自动创建他们,在执行像mvn clean install这样的命令时,只要按照约定,我们不需要额外配置什么,这条命令就可以用来构建几乎任何的Maven项目。
反应堆:
反应堆是指所有模块组成的一个构建结构,包含了各个模块之间的依赖和继承关系,能够计算出合理的模块构建顺序。
构建顺序:按照聚合项目中中声明元素的顺序来读取各个子模块的POM文件,如果POM文件没有依赖模块(父模块),那么就构建该模块,否则就先构建其依赖模块,如果该依赖还依赖于其他模块,则进一步先构建依赖的依赖模块。
当我们想仅仅构建完整反应堆中的某些模块时,我们需要调用以下命令:
- -am(also-make):同时还会构建所列项目的依赖模块(父模块)
- -amd(also-make-dependents):同时还会构建所列模块的子模块
- -pl(-project) : 构建指定模块,模块间用逗号分隔
- -rf(resume-from) :在完整的反应堆构建顺序基础上指定从哪个模块开始构建
假设有child-A,child-B依赖于parent,完整的反应堆构建顺序为:parent ---> child-A —> child-B
mvn clean install -pl child-A -am:此时会先构建parent,在构建child-A
mvn clean install -pl child-A,child-B:此时会构建child-A和child-B,但是不会构建
parent
mvn clean install -pl parent -amd:此时会先构建parent,然后再构建child-A和child-B
mvn clean install -pl child-A -amd:此时会从完整的反应堆构建顺序的child-A开始到child-B