一、聚合
想要一次构建两个项目,而不是到两个模块目录下分别执行mvn命令。
为了能使用一条命令构建两个模块,需要创建一个额外的模块account-aggregator,然后通过该模块构建整个项目的所有模块。这个account-aggregator模块作为一个Maven项目,有自己的POM,同时作为聚合项目,又有特殊点:
<project xmlns="...">
<modeVersion>4.0.0</modeVersion>
<groupId>xxx.xxx.xxx</groupId>
<artifactId>account-aggregator</artifactId>
<version>1.1.0-snapshot</version>
<packaging>pom</packaging>
<name>xxx xxx</name>
<modules>
<module>account-email</module>
<module>account-persist</module>
</modules>
/project>
特殊的地方之一为packaging,值为pom。对于聚合模块来说,其打包发送packaging值必须为pom,否则无法构建。
元素modules,是实现聚合的最核心的配置。可以通过在一个打包发送为pom的Maven项目中声明容易数量的module元素来实现聚合模块。每个module的值都是一个当前POM的相对目录。
一般来说,为了方便快速定位内容,模块所处目录名称应当与其artifactId一致,不过这不是Maven的要求。
为了方便构建项目,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在。不一定要是父子关系,可以是平行目录。
如果使用平行目录结构,聚合模块的POM需要修改,指向正确的目录:
<modules>
<module>../account-email</module>
<module>../account-persist</module>
</modules>
聚合模块仅有一个pom.xml文件,仅仅帮助聚合其他模块构建。最后在聚合模块运行mvn clean install命令。
Maven首先解析聚合模块的POM、分析要构建的模块、计算出一个反应堆构建顺序,然后根据这个顺序依次构建各个模块。
在POM中配置合理的name字段,目的是让Maven构建输出更清晰。
二、继承
抽取重复的配置。需要创建POM的父子结构,然后在父POM中声明一些配置供子POM继承,实现“一处声明,多处使用”。
继续以上面的为基础,在account-aggregator下创建一个名为account-parent的子目录,然后在该目录下建立一个除account-aggregator之外的父模块。为此,在该子目录建一个pom.xml文件:
<project xmlns="...">
<modelVersion>4.0.0</modulVersion>
<groupId>xxx.xxx.xxx</groupId>
<artifactId>acount-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>Account Parent</name>
</project>
它使用和其他模块一致的groupId和version,使用artifactId为account-parent表示父模块。packaging为pom。不包含除POM以外的文件。
父模块需要其他模块继承。修改子模块的POM:
...
<parent>
<groupId>xxx.xxx.xxx</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-snapshot</version>
<relativePath>../account-parent/pom.xml</relativePath>
</parent>
...
使用parent元素声明父模块,parent下的groupId、artifactId和version指定父模块坐标。relativePath元素表示父模块POM的相对路径。当项目构建时,Maven会根据relativePath检查父POM,如果找不到,再从本地仓库找。默认值是../pom.xml,即默认父POM在上层目录下。
更新过的POM没有为account-email声明groupId和version,这个子模块隐式的从父模块继承了这两个元素。子类也可以显示声明该两个元素不同的值。对于artifactId元素,子模块应该显式声明。
可继承的POM元素
![](https://img.haomeiwen.com/i9449419/f58ccbb47f73cd9f.png)
![](https://img.haomeiwen.com/i9449419/4deda934b2101c67.png)
依赖管理
Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活。在dependencyManagement下的依赖声明不会引入实际的依赖,不过能约束dependencies下的依赖使用。在account-parent中加入该配置:
...
<properties>
<springframework.version>2.5.6</springframework>
<junit.version>4.7</junit.version>
</properties>
<dependencyManagement>
<dependencies>
...
</dependencies>
</dependencyManagement>
...
这段配置会被继承。修改account-email的POM:
<propeties>
...
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
...
</dependencies>
所有的springframework依赖只配置了groupId和artifactId,junit省去了version和scope。原因是继承了父pom的dependencyManagement配置。
如果子模块不声明依赖的使用,即使父POM声明了该依赖,也不会产生实际效果。
import范围依赖,该依赖只中dependencyManagement元素下才有效,使用该范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement中。
<dependencyManagement>
<dependencies>
<dependency>
...
<type>pom</type>
<scpoe>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
插件管理
提供pluginManagement元素管理插件。在该元素配置的插件不会造成实际插件调用,当pom中配置了真正的plugin元素,并且其groupId和artifactId与该元素中配置的插件匹配时,该元素配置才会影响实际插件效果。
如果子模块不需要使用父模块中pluginManagement配置的插件,可以忽略。需要配置不同插件,可以自行配置覆盖父模块配置。
四、聚合与继承关系
聚合目的是为了方便快速构建项目,继承为了消除重复配置。
聚合与继承的父模块的POM的packaging必须是pom;聚合模块和继承关系的父模块只有POM。
五、约定优于配置
约定可以大量减少配置。Maven假设目录:
- 源码目录:src/main/java
- 编译输出目录:target/classes/
- 打包方式:jar
- 包输出目录:target/
Maven允许自定义源码目录,除非特殊情况,不建议这样做。
Maven设定核心插件原因是防止由于插件版本的变化而造成构建不稳定。
六、反应堆
在一个多模块的Maven项目中,反应堆(Reactor)指所有模块组成的一个构建结构。对于单模块项目,反应堆就是本身,对于多模块项目,反应堆包含各模块间的继承和依赖关系,从而能计算出合理的模块构建顺序。
1.反应堆构建顺序
修改account-aggregator聚合配置如下:
<module>
<module>account-email</module>
<module>account-persist</module>
<module>account-parent</module>
</modules>
POM的读取次序不足以决定反应堆构建顺序,要考虑模块间的依赖和继承关系。
![](https://img.haomeiwen.com/i9449419/97184e89fccbfbe3.png)
实际的构建顺序是:Maven按序读取POM,如果该POM没有依赖模块,那么构建该模块,否则先构建其依赖模块,如果该依赖还依赖于其他模块,则进一步构建依赖的依赖。
2.裁剪反应堆
Maven提供命令行选项裁剪反应堆,mvn-h可以看到这些选项:
![](https://img.haomeiwen.com/i9449419/4c68259b3e05e17c.png)
在-pl -am或者-pl -amd基础上,还能应用-rf参数,以对裁剪后的反应堆再次裁剪。
网友评论