本文将在以下三部分进行编写:
- POM文件解析
- 构建顺序
- 可选择性部署
Maven聚合即可以将多个子模块聚合在一起进行构建,继承即所有子模块均可以继承父模块的属性、依赖和插件等,可消除重复配置。
Maven基础性的知识不在此赘述,本文中涉及的Maven关键标签包括:
- packaging:打包方式,常见的有jar(默认)、war和pom等;
- module:子模块声明
- properties:属性,可定义依赖报的版本号、定义某些插件的配置等;
- dependencyManagement:依赖管理,具有依赖管理的POM,即不会给父模块引入依赖,也不会给子模块引入依赖。只有子模块声明依赖的使用(只需groupId和artifactId即可指明所用依赖),才会引入依赖包。使用import范围依赖导入依赖管理配置暂不讨论;
- pluginManagement:插件管理,与依赖管理类似,该元素的配置不会实际插件调用的行为,只有当POM中正在配置对应的plugin元素才起作用;
- repositories:用于声明远程仓库,可以是私有仓库(可由nexus搭建)。所有POM文件都会继承Maven顶级POM,其中包含Maven中央仓库。
- distributionManagement:部署管理,可部署到远程Maven仓库,支持snapshotRepository和repository分别对应快照和稳定版本。
举一个比较常见的例子,假设某项目project包含三个模块,分别是:project_a、project_b、project_c,其文件结构如下,三个子模块与pom.xml在同一级,与父模块是父子目录结构:
/project
/project_a
/project_b
/project_c
pom.xml
1. POM文件解析
父模块project
1 <?xml version="1.0" encoding="UTF-8"?> 2 <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"> 3 <modelVersion>4.0.0</modelVersion> 4 <groupId>com.test</groupId> 5 <artifactId>project</artifactId> 6 <version>0.0.1-SNAPSHOT</version> 7 <packaging>pom</packaging> 8 <modules> 9 <module>project_a</module> 10 <module>project_b</module> 11 <module>project_c</module> 12 </modules> 13 14 <properties> 15 <spring.version>4.3.1.RELEASE</spring.version> 16 </properties> 17 18 <dependencyManagement> 19 <dependencies> 20 <dependency> 21 <groupId>org.springframework</groupId> 22 <artifactId>spring-aop</artifactId> 23 <version>${spring.version}</version> 24 </dependency> 25 ...... 26 <dependency> 27 <groupId>org.springframework</groupId> 28 <artifactId>spring-core</artifactId> 29 <version>${spring.version}</version> 30 </dependency> 31 </dependencies> 32 </dependencyManagement> 33 34 <build> 35 <pluginManagement> 36 <plugins> 37 <plugin> 38 <groupId>org.apache.maven.plugins</groupId> 39 <artifactId>maven-compiler-plugin</artifactId> 40 <version>3.1</version> 41 <configuration> 42 <source>1.7</source> 43 <target>1.7</target> 44 </configuration> 45 </plugin> 46 ...... 47 <plugin> 48 <groupId>org.apache.maven.plugins</groupId> 49 <artifactId>maven-surefire-plugin</artifactId> 50 <version>2.12.4</version> 51 <configuration> 52 <skipTests>true</skipTests> 53 </configuration> 54 </plugin> 55 </plugins> 56 </pluginManagement> 57 </build> 58 59 <repositories> 60 <repository> 61 <id>nexus</id> 62 <name>maven repository</name> 63 <url>http://serverip:port/nexus/content/groups/public/</url> 64 <snapshots> 65 <enabled>true</enabled> 66 </snapshots> 67 <releases> 68 <enabled>true</enabled> 69 </releases> 70 </repository> 71 </repositories> 72 73 <!-- 发布 构件 到 私服 --> 74 <distributionManagement> 75 <repository> 76 <id>nexus</id> 77 <name>releases</name> 78 <url>http://serverip:port/nexus/content/repositories/releases/</url> 79 </repository> 80 81 <snapshotRepository> 82 <id>nexus</id> 83 <name>snapshots</name> 84 <url>http://serverip:port/nexus/content/repositories/snapshots/</url> 85 </snapshotRepository> 86 </distributionManagement> 87 </project>
- 4-6行:声明该构件在Maven仓库中的唯一坐标;
- 7行: 打包方式为pom,起聚合作用的父模块必须声明为pom方式,否则构建失败;
- 8-12行:聚合三个子模块project_a、project_b、project_c,每个module的值代表当前POM的相对目录,如果三个子模块与父模块是平行目录结构,则值应该为 ../project_a;
- 14-16行:定义POM属性,其可由子模块继承,在此声明为spring版本号;
- 18-32行:配置依赖管理,对工程不会引入依赖,只有真正声明对应依赖才会引入;
- 34-57行:配置插件管理,同依赖管理;
- 59-71行:配置远程Maven仓库,需要在setting.xml中配置远程Maven仓库的用户名与密码等信息,如下:
<servers> <server> <id>nexus</id> <username>admin</username> <password>admin123</password> </server> </servers>
- 74-86行:发布构件到私服配置,其他开发者可以使用该构件
子模块(project_a)
1 <?xml version="1.0"?> 2 <project 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" 4 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 5 <modelVersion>4.0.0</modelVersion> 6 <parent> 7 <groupId>com.test</groupId> 8 <artifactId>project</artifactId> 9 <version>0.0.1-SNAPSHOT</version> 10 </parent> 11 <artifactId>project_a</artifactId> 12 <name>project_a</name> 13 14 <dependencies> 15 <dependency> 16 <groupId>org.springframework</groupId> 17 <artifactId>spring-aop</artifactId> 18 </dependency> 19 ...... 20 <dependency> 21 <groupId>org.springframework</groupId> 22 <artifactId>spring-core</artifactId> 23 </dependency> 24 </dependencies> 25 26 <build> 27 <plugins> 28 <plugin> 29 <groupId>org.apache.maven.plugins</groupId> 30 <artifactId>maven-compiler-plugin</artifactId> 31 </plugin> 32 ...... 33 <plugin> 34 <groupId>org.apache.maven.plugins</groupId> 35 <artifactId>maven-surefire-plugin</artifactId> 36 </plugin> 37 </plugins> 38 </build> 39 </project>
- 6-10行:由parent元素声明该模块的父模块为project,其中省略了<relativePath>,该元素标识父模块POM的相对位置(由父模块与子模块的目录结构决定),默认值为 ../pom.xml,在本例子中为默认值;
- 11-12行:由于继承了父模块中的所有配置,此处只需要声明artifactId即可,groupId与version继承自父模块。name元素用于描述,在构建日志找那个将显示该名;
- 14-24行:真正声明所需依赖,会为该模块引入对应依赖,只需groupId和artifactId;
- 26-38行:真正声明所需插件,也只需groupId和artifactId;
2. 构建顺序
在对工程只需 mvn clean install等命令的时候,Maven会首先解析聚合模块的POM、分析要构建的模块、并计算出一个反应堆构建顺序(Reactor Build Order),然后根据这个顺序依次构建模块。其中反应堆是所有模块组成的一个构建结构,包含各模块之间的继承与依赖关系,计算合理的模块构建顺序。(模块构建具体经历过哪些过程,可学习Maven中的生命周期)。
假设三个模块之间存在一定的依赖关系,如图:
本工程的构建顺序如下,聚合模块project、project_a、project_c、project_b:
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] project
[INFO] project_a
[INFO] project_c
[INFO] project_b
[INFO]
[INFO] ------------------------------------------------------------------------
还可以通过命令行对反应堆进行裁剪,包括:
- -am, --also-make 同时构建所列模块的依赖模块
- -amd, --also-make-dependents 同时构建依赖于所列模块的模块
- -pl, --projects <arg> 构建指定的模块,模块间用逗号分隔
- -rf,-resume-form <arg> 从指定模块开始构建
举例:mvn clean install -pl project_a,project_b
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] project_a
[INFO] project_b
[INFO]
[INFO] ------------------------------------------------------------------------
mvn clean install -pl project_a -am
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] project
[INFO] project_a
[INFO]
[INFO] ------------------------------------------------------------------------
mvn clean install -pl project_a -amd
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] project_a
[INFO] project_c
[INFO] project_b
[INFO]
[INFO] ------------------------------------------------------------------------
mvn clean install -pl project_a -amd -rf project_c
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] project_c
[INFO] project_b
[INFO]
[INFO] ------------------------------------------------------------------------
3. 可选择性部署
在父模块中声明了distributionManagement,在执行 mvn deploy的时候,会将所有的模块部署到远程服务器上。如何做到有选择性的的部署呢?比如不想将project_a部署到Maven仓库,有两种方式:
(1)在properties元素中声明 maven.deploy.skip
在project_a模块的POM文件中添加如下配置:
<properties> <maven.deploy.skip>true</maven.deploy.skip> </properties>
(2)声明子模块插件,覆盖父模块插件
在project_a模块的POM文件中,添加属于自己的部署插件:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <version>2.4</version> <configuration> <skip>true</skip> </configuration> </plugin>
注意: 在部署的时候,不想将父POM工程部署到远程仓库,此时怎么办?需要注意的是如果在父POM中声明跳过部署,则所有子POM工程继承该属性而跳过部署。解决方案:父POM声明跳过部署,子POM覆盖该属性,声明为false。
总结
- POM的聚合与继承,前者方便快捷构建项目,后者可以消除重复配置;
- 除了本文举的例子,可以在聚合模块任意文件夹什么父模块,只需要改变module和relativePath的相对路径即可;
- 根据反应堆对各模块间依赖关系的计算,形成合理的构建顺序,并可以对构建顺序进行裁剪;
- 可以通过插件属性配置,实现模块可选择的部署;