文章目录
#1 聚合
- Maven聚合(或者称为多模块)是为了实现多模块的组合。为了能够实现模块证号,我们需要创建一个额外的聚合模块,然后通过该模块构建整个项目的所有模块。聚合模块本身作为一个Maven项目,它必须要有自己的POM,不过,同时作为一个聚合项目,其POM又有特殊的地方。
- packaging,其值为POM。
- 元素modules,这是实现聚合的最核心的配置。用户可以通过在一个打包方式为pom的Maven项目中声明任意数量的module元素来实现模块的聚合。这里每个module的值都是一个当前POM的相对路劲。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.companyname.myapp</groupId>
<artifactId>project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Project-Aggregator</name>
<modules>
<module>project-a</module>
<module>project-a</module>
</modules>
</project>
-
为了方便用户构建项目,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在,这样当用户得到源码的时候,第一眼发现的就是聚合模块的POM,不用从多个模块中去寻找聚合模块来构建整个项目。
#2 继承 -
在面向对象世界中,程序员可以使用类继承在一定程度上消除重复,在Maven的世界中,也有类似的机制能让我们抽取出重复的配置,这就是POM的继承。
-
作为父模块的POM,其打包类型也必须为pom
由于父模块只是为了帮助消除配置的重复,因此它本身不包含除POM之外的项目文件,也就不需要src/main/java/之类的文件夹了。有了父模块,就需要让其他模块来继承它。 -
父模块:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.companyname.myapp</groupId>
<artifactId>project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Project-parent</name>
</project>
- 子模块:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.companyname.myapp</groupId>
<artifactId>project-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../project-parent/pom.xml</relativePath>
</parent>
<artifactId>project</artifactId>
<name>Project-childen</name>
</project>
- 配置中使用parent元素声明父模块,parent下的子元素groupId、artifactId和version指定了父模块的坐标,这三个元素是必须的。元素relativePath表示父模块POM的相对路径。
- 可被继承的POM元素:
- groupId:项目组ID,项目坐标的核心元素。
- version:项目版本,项目坐标的核心元素。
- description:项目的描述信息。
- organization:项目的组织信息。
- inceptionYear:项目的创始年份。
- url:项目的URL地址。
- developers:项目的开发者信息。
- contributors:项目的贡献者信息。
- distributionManagement:项目的部署配置。
- issueManagement:项目的缺陷跟踪系统信息。
- ciManagement:项目的持续集成系统信息。
- scm:项目的版本控制系统信息。
- mailingLists:项目的邮件列表信息。
- properties:自定义的Maven属性。
- dependencies:项目的依赖配置。
- dependencyManagement:项目的依赖管理配置。
- repositories:项目的仓库配置。
- build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等。
- reporting:包括项目的报告输出目录配置、报告插件配置等。
#3 依赖管理
- Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖(父模块只管定义,不管使用),不过它能够约束dependencies下的依赖使用。如果子模块不声明依赖的使用,即使该依赖已经在父POM的dependencyManagement中声明了,也不会产生任何实际的效果。
- 父模块:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.companyname.myapp</groupId>
<artifactId>project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Project-parent</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
- 子模块:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.companyname.myapp</groupId>
<artifactId>project-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../project-parent/pom.xml</relativePath>
</parent>
<artifactId>project</artifactId>
<name>Project-childen</name>
<dependencies>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
</dependency>
</dependencies>
</project>
-
子模块继承父模块,只需要指明groupId和artifactId即可。
#4 插件管理 -
Maven提供了pluginManagement元素帮助管理插件。在该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。如果子模块不需要使用父模块中pluginManagement配置的插件,可以尽管将其忽略。如果子模块需要不同的插件配置,则可以自行配置以覆盖父模块的pluginManagement配置。
-
当项目中的多个模块有同样的插件配置时,应当将配置移到父POM的pluginManagement元素中。即使各个模块对于同一插件的具体配置不尽相同,也应当使用父POM的pluginManagement元素统一声明插件的版本。甚至可以要求将所有用到的插件的版本在父POM的pluginManagement元素中声明,子模块使用插件时不配置版本信息,这么做可以统一项目的插件版本,避免潜在的插件不一致或者不稳定问题,也更易于维护。
#5 聚合和继承的关系
多模块Maven项目中的聚合与继承其实是两个概念,其目的完全是不同的。前者主要是为了方便快速构建项目,后者主要是为了消除重复配置。对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。对于继承关系的父POM来说,它不知道有哪些子模块继承于它,但那些子模块都必须知道自己的父POM是什么。如果非要说这两个特性的共同点,那么可以看到,聚合POM与继承关系中的父POM的packaging都必须是pom,同时,聚合模块与继承关系中的父模块除了POM之外都没有实际的内容。
#6 反应堆
在一个多模块的Maven项目中,反应堆(Reactor)是指所有模块组成的一个构建结构。对于单模块的项目,反应堆就是该模块本身,但对于多模块项目来说,反应堆就包含了各模块之间继承与依赖的关系,从而能够自动计算出合理的模块构建顺序。
实际的构建顺序是这样形成的:Maven按序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块,如果该依赖还依赖于其他模块,则进一步先构建依赖的依赖。模块间的依赖关系会将反应堆构成一个有向非循环图(Directed Acyclic Graph,DAG),各个模块是该图的节点,依赖关系构成了有向边。这个图不允许出现循环,因此,当出现模块A依赖于B,而B又依赖于A的情况时,Maven就会报错。
一般来说,用户会选择构建整个项目或者选择构建单个模块,但有些时候,用户会想要仅仅构建完整反应堆中的某些个模块。换句话说,用户需要实时地裁剪反应堆。Maven提供很多的命令行选项支持裁剪反应堆,输入mvn -h可以看到这些选项: -
-am,——also-make同时构建所列模块的依赖模块
-
-amd-also-make-dependents同时构建依赖于所列模块的模块
-
-pl,——projects<arg>构建指定的模块,模块间用逗号分隔
-
-rf-resume-from<arg>从指定的模块回复反应堆