一、聚合
假设有两个模块:account-email和account-persist;
能够使用一条命令就能构建上述两个模块,需要创建一个额外的模块:account-aggregator;
通过account-aggregator构建整个项目的所有模块,而该模块本身也是Maven项目,有自己的Pom文件;
注意聚合模块account-aggregator打包方式packaging的值必须为pom,如下:
注意,聚合模块与其他模块的目录结构一般是父子关系,如上述pom所示。不过也不是必须的,这是要注意指向正确的模块目录(modules/module中的相对路径)。
此时,就可以从聚合模块运行命令进行构建各个模块,构建顺按照后面将要介绍的反应堆构建顺序进行:
mvn clean install
二、继承
目的:抽取重复的配置,在一定程度上消除重复。
步骤:
1、在account-aggregator下创建一个目录account-parent最为父模块的目录,并在该父目录下建立pom.xml文件如下:
注意,packaging必须为pom;
另外,父模块为了消除配置的重复,因此本身不包含除POM之外的项目文件(如不需要src/main/java之类的文件夹)。
2、其他模块继承父模块
如在子模块account-email中的pom.xml配置如下:
注意GAV:
子模块可以继承父模块的groupId和/或version,也可以显式声明与父模块不一样的数值;
但是,artifactId必须要在子元素中显式声明,且与父模块不同。
另外,可以继承的POM元素很多,不只GAV。
3、把account-parent加入到聚合account-aggregator中,如下:
依赖管理:
Maven提供的dependencyManagement元素既能让子模块继承到父摸块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。例如,可以在account-parent中加入:
这里使用dependencyManagement声明的依赖既不会给account-parent引入依赖,也不会给它的子模块引入依赖不过这段配置是会被继承的,如在account-email中:
上述POM中的依赖配置较原来简单了一些,所有的springframework依赖只配置了groupId和aitifactId,省去了version,junit依赖不仅省去了version,还省去了依赖范围scope。
使用这种依赖管理机制似乎不能减少太多的POM配置,不过笔者还是强烈推荐采用这种方法。其主要原因在于在父POM中使用dependencyManagement声明依赖能够统一项目范围中依赖的版本,不会发生多个子模块使用依赖版本不一致情况,减少依赖冲突。
当然,如果子模块不声明依赖的使用,即使依赖已经在父POM的dependencyManagement中声明,也不会产生任何实际的效果。
插件管理:
Maven提供了dependencyManagement元素帮助管理依赖,类似地,Maven也提供了pluginManagement元素帮助管理插件。在该元素中配置的依赖不会造成实际的插件调用行为,
当POM中配置了真正的plugin元素,并且其groupld和artifactld与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。
三、聚合与继承的关系
多模块Maven项目中的聚合与继承其实是两个概念,其目的完全是不同的。前者主要是为了方便快速构建项目,后者主要是为了消除重复配置;
对于聚合模块来说,它知道有些被聚合的模块,但那此被聚合的模块不知道这个聚合模块的存在;
对于继承关系的父POM来说,它不知道有哪此子模块继承于它,但那些子模块都必须知道自己的父POM是什么。
实际项目中,读者会发现一个POM既是聚合POM,又是父POM,这么做主要为了方便。
四、约定优于配置
定义:Maven提倡“约定优于配置”(Convention Over Configuration)。
这不是说Maven约定的规则会忽略用户配置的规则,而是说不推荐用户配置一些规则,而是使用约定的规则。如Maven默认约定用户的项目是这样的:
当然,Maven允许自定义源码目录:
原因:超级POM
任何一个Maven项目都隐式地继承一个POM,就像任何JAVA类继承Object类一样,这个被继承的POM中的配置就成为Maven所提倡的约定。
Maven3中,这个超级POM文件在MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路径下。
例如,超级POM关于仓库的定义:
五、反应堆
定义:
在一个多模块的Maven项日中,反应堆( Reactor)是指所有模块组成的一个构建结构。
对于单模块的项目,反应堆就是该模块本身。但对于多模块项目来说,反应堆就包含了各模块之间继承与依赖的关系,从而能够自动计算出合理的模块构建顺序。
原理:
实际的构建顺序是这样形成的:Maven按序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块。如果该依赖还依赖于其他模块,则进一步先构建依赖的依赖。
模块间的依赖关系会将反应堆构成一个有向非循环图(Directed Acyclic Graph,DAG),各个模块是该图的节点,依锁关系构成了有向边。这个图不允许出现循环,因此,当出现模块A依赖于B,而B又依赖于A的情况时, Maven就会报错。
裁剪反应堆:
有时用户会想仅仅构建完整反应堆中的某些模块,即裁剪反应堆。
如:
mvn clean install -pl account-email,account-persist