聚合
当我们有多个子模块的时候,我们应该不会想着去点每个子模块单独去执行构建,而是想通过一条mvn
命令实现所有模块的构建。将多个子模块 合并 到一个模块里去的功能,就是聚合。聚合的实现很简单,就是创建一个项目,在这个项目里声明一个<modules>
元素 并在其中声明多个模块:
<modules>
<module>oa-organ</module>
<module>oa-auth</module>
<module>oa-flow</module>
<module>oa-web</module>
</modules>
此时,做一些test
、compile
、install
等操作都能同时对这些子模块进行操作。
如果使用IDE,建议最好一开始就建立聚合关系,否则需要IDE重新构建这个项目(因为创建一个项目时,大多数都已经分配好了结构)
另外,【聚合】 和 【继承】 是两个概念,【聚合】仅仅只是将多个模块放到一个模块里,然后通过一个模块控制多个模块。聚合有两种目录结构模式:
聚合模块的父子目录结构 | 聚合模块的平行目录结构 |
---|---|
聚合的小坑
在聚合多个模块的项目下,如果发生了新增子模块、删除子模块操作,请务必重新install <parent>
,否则会报找不到依赖:
<坐标> was cached in the local repository, resolution will not be reattempted until the update interval of <远程仓库名字> has elapsed or updates are forced
继承
如果有许多公共的组件每个模块都需要使用,那么在所有的子模块中都得重复定义很多遍,但是若有了【继承】,就只需要定义一遍。子项目都可以通过<parent>
元素继承父模块,从而实现所有的依赖、属性都能够被子模块直接继承。比如父项目的pom.xml
如下所示:
<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.zhss.oa</groupId>
<artifactId>oa-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>oa-parent</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springframework.release.version>3.2.8.RELEASE</springframework.release.version>
<jackson.version>2.9.2</jackson.version>
</properties>
<dependencies>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.release.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.release.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.release.version}</version>
</dependency>
</dependencies>
</project>
其中重点关注<packaging>pom</packaging>
、以及<properties>
元素和<dependencies>
元素。
父模块只是为了帮助消除重复的配置,因此它本身不需要包含除pom.xml文件以外的项目文件。
然后再看子项目的pom.xml
如下所示:
<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>
<artifactId>oa-flow</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.zhss.oa</groupId>
<artifactId>oa-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
...
</project>
这个子项目的pom.xml
中继承了oa-parent
,那么oa-parent
中的所有<dependencies>
元素、<properties>
元素都会被这个子项目oa-flow
继承。
哪些元素可以被子模块继承
可被子模块继承的元素 | |||
---|---|---|---|
**groupId** |
项目组ID(重要) | ciManagement |
项目持续继承配置 |
**version** |
项目版本(重要) | scm |
项目版本控制配置 |
description |
项目描述信息 | mailingLists |
邮件列表信息 |
organization |
项目组织信息 | proerties |
自定义的Maven属性 |
inceptionYear |
项目的创始年份 | dependencies |
项目的依赖配置 |
url |
项目的URL地址 | dependencyManagement |
项目的依赖管理配置 |
developers |
项目开发者信息 | repositories |
项目的仓库配置 |
contributors |
项目贡献者信息 | build |
项目的源码目录、输出目录、插件配置、插件管理等等 |
distributionManagement |
项目的部署配置 | reporting |
|
issueManagement |
缺陷跟踪系统配置 |
实战技巧之单独编译子模块
如果我们有很多个子模块(未安装到本地仓库),但是我只想编译一个子模块时,因为父模块还没有安装到本地仓库中,所以就会因为找不到【父pom.xml】而失败。因此有了一种这样的做法:
<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>
<artifactId>oa-flow</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.zhss.oa</groupId>
<artifactId>oa-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- 指向父pom.xml -->
</parent>
...
</project>
通过<relativePath>
元素直接声明【父pom.xml】,那么在单独编译某个模块时就不会有这样的问题了。【Maven】会直接按照这个路径去找到【父pom.xml】。
实战技巧之版本号分歧
当子模块都继承了父模块后,子模块的groupId
、version
都已经隐式的从父模块里继承过来了,倘若子模块的版本号和父模块的版本号产生了分歧,只需要 在子模块中显式声明 即可覆盖。
聚合与继承的关系
- 对于聚合模块来说,【聚合模块】知道有哪些【被聚合模块】,但是【被聚合模块】不知道这个【聚合模块】
- 对于继承模块来说,【父pom.xml】不知道哪些【子pom.xml】继承它,【子pom.xml】知道自己继承了哪个【父pom.xml】
两者的唯一共同点就是 【聚合模块】 和 【父pom.xml】 的packaging
都必须是pom
类型,而且它们除了【pom.xml】都没有实际的内容
依赖管理
对于【继承】功能来说,dependencies
元素会被无条件全部继承,如果一个模块想要这些依赖,另个模块想要那些依赖:
A工程
:依赖com.xuxueli:xxl-job-core
、cn.jpush.api:jpush-client
、dom4j:dom4j
B工程
:依赖com.xuxueli:xxl-job-core
、commons-codec:commons-codec
(假设这些依赖【父pom.xml】中都有)通过【继承】功能来做的话,A工程
里会引入五个依赖,B工程
也会引入五个依赖,即使有些依赖它们并用不到。换言之,【继承】功能下的<dependencies>
元素太暴力了,强制子模块都继承自己的依赖。所幸【Maven】提供了另一种解决方案——依赖管理。
依赖管理功能是通过 <depdendencyManagement>
元素来实现的,它既提供了一部分的约束性,又提供了一部分的灵活性,让开发人员得到了最高的灵活性。我们在【父pom.xml】中把原先在<dependencies>
元素挪到<dependencyManagement>
中:
...
<!-- parent 父工程 -->
<dependencyManagement>
<dependencies>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.release.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.release.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.release.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
...
使用
<dependencyManagement>
元素后并不会引入这些依赖,而只是单纯的声明而已
随后在子工程中仍然声明<dependencies>
元素(原先使用【继承】不用声明):
<dependencies>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<!-- 在子工程中没有声明 spring-webmvc,所以不会引入这个包 -->
</dependencies>
这个方式的好处是子工程需要哪些依赖就声明哪个依赖即可,但是不用声明**版本**
,因为版本已经被【父pom.xml】统一约束起来了(让子模块继承<dependencyManagement>
元素来实现的)。所以子工程就能直接继承父工程的版本号。在规范的工程管理中,肯定都是用<dependencyManagement>
和<pluginManagemnent>
的。
强制约束依赖方的版本号
前面提到了通过【<dependencyManagement>
元素 + 继承】来约束子工程的版本号,这是强制约束子工程的版本号。如果想要约束依赖方的版本号(项目和项目之间的依赖)怎么办呢?考虑这样一个场景:
- 你开发了一个第三方的一个组件,就是一个不属于任何一个项目的基础的一个组件,比如基于
**activiti**
开发了一个流程审批的封装以后的一个组件。其他人想要使用你这个组件,必然要在<denpendencies>
里声明依赖你的组件,倘若这个依赖方没有注意又自己引入了一个activiti1.2
(你的组件是activiti1.3
),按照依赖调节原则,Maven
会去选择依赖方的activiti1.2
版本,如此容易造成依赖冲突。
<dependencyManagement>
的另外一种玩法就是“导入”:
<!-- common-flow,这是你自己开发的三方组件 -->
...
<!-- common-flow-bom,中间POM -->
<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">
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.zhss.commons</groupId>
<artifactId>commons-flow</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
依赖方首先依赖common-flow
这个依赖,然后再在自己的pom.xml
中声明<dependencyManagement>
元素:
<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">
...
<dependencies>
...
<dependency>
<groupId>com.zhss.commons</groupId>
<artifactId>commons-flow</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.zhss.commons</groupId>
<artifactId>commons-flow-bom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
在<dependencyManagement>
元素中声明的<dependency>
有两个关注点:
type=pom
,表示这个依赖只是一个pom
文件scope=import
,表示导入这个pom
文件(scope=import
仅在<dependencyManagement>
里生效)
实际上的效果会如下所示:
<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">
...
<dependencies>
...
<dependency>
<groupId>com.zhss.commons</groupId>
<artifactId>commons-flow</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.zhss.commons</groupId>
<artifactId>commons-flow</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
插件管理
【Maven】不仅提供了【依赖管理】功能来管理依赖还提供了【插件管理】来帮助管理插件。同<dependencyManagement>
一样,<pluginManagement>
里的配置不会造成实际的引入和插件调用。
<!-- parent 父pom -->
<build>
...
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
<configuration>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
这样只是声明了这个插件的属性,并不对产生任何实际的影响。如果想要真正使用它,就需要在<build><plugins>
里声明对应的<plugin>
即可使用:
<!-- 子工程 -->
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
</plugins>
</build>
此时,在子工程中只需要定义一下这个插件,就可以使用【父pom.xml】里配置好的插件。如果不想使用父模块的<pluginManagement>
,那么直接在里面覆盖即可。
如果项目中多个模块都有同样的插件配置时,最好将配置都移到【父pom.xml】的
<pulginManagement>
元素中。即使各个模块对同一插件的具体配置不尽相同,也应当在【父pom.xml】的<pluginManagement>
元素里统一声明插件版本。这样更易维护和维持稳定。
设置变量统一版本号
简单来说就是通过给定一个变量,让所有的依赖的<version>
设置为此变量,从而实现修改一个变量,就能让所有依赖的版本变更。
<!-- parent -->
<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">
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springframework.release.version>3.2.8.RELEASE</springframework.release.version>
<jackson.version>2.9.2</jackson.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.release.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.release.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
拓展知识
正常来说【Maven】的目录结构应该按照约定进行使用:
main
是放主程序java代码和配置文件java
是放你的程序包和包中的java文件resources
是放你的java程序中要使用的配置文件test
是放测试程序代码和文件的(可以没有),test
下的java
和resources
文件夹都仅在测试阶段生效。pom.xml
是maven
的核心文件(maven
项目必须有)
如果想要自定义配置目录结构,需要这样配置:
<project>
...
<build>
<sourceDirectory>src/java</sourceDirectory>
<testOutputDirectory>...</testOutputDirectory>
<testSourceDirectory>...</testSourceDirectory>
</build>
</project>