在说明如何使用Maven模块化管理的继承和聚合机制之前,需要先澄清一些概念性的东西:
1.Maven是什么?
2.什么是Maven模块化管理?
3.Maven模块化管理的继承和聚合机制是什么含义?
Maven是什么
Maven是Java项目开发中使用的一种依赖包管理工具,与其功能相似的还有另外一个叫做Gradle的工具。在没有Maven之前,Java项目的依赖包管理是需要将jar包拷贝到项目源码中的,在依赖包多的情况下,将会使得项目源码非常庞大,也不利于团队协作。使用Maven或者Gradle可以将项目依赖声明在一个指定的配置文件中,这样就无需手动将依赖jar包拷贝到项目源码中了,一方面项目源码不再无限增大,另一方面能起到一次声明到处构建的效果,这样大大提高了团队协作的效率。
依赖包管理是Maven最核心最基础的功能,除此之外,还能使用Maven实现项目构建和打包,通过插件化的机制实现代码检查,单元测试等等,功能非常强大。
什么是Maven模块化管理
在项目开发过程中,常常有这种需求:将一些通用的工具类方法提取出来,方便在多个地方调用。但是仅仅这样做只能在一个项目中使用,当需要在多个项目中使用这些通用的工具方法时,就不得不将这些工具类方法再重复定义一次。这样既产生了编码重复,也为方法的管理带来了新的挑战(如:如何统一升级方法实现等)。为了实现在多个Java项目中共用一些通用的工具类方法,Maven提供了一种模块化的机制,可以将这些通用的工具类方法放到一个模块中,然后将这个模块打包为独立的jar包,在其他项目中引用这个公共的jar包即可。
Maven模块化管理的继承和聚合机制是什么含义
所谓继承,就是可以在一个“父模块”中统一声明依赖信息,以及一些其他参数,这样在子模块中就可以直接引用即可,实现了在相同的“父模块”中实现统一的依赖版本管理,便于依赖jar包的版本冲突解决。
所谓聚合,就是提供了一种机制可以实现多个Maven模块的统一构建,模块间的依赖更新等问题,用于提升开发效率。
虽然继承和聚合是两个独立的概念,但是他们也有一些共同点:
1.聚合与继承的实现都需要有一个父模块,父模块打包方式均为pom
2.聚合与继承均属于设计型模块,并无实际的模块内容
如何使用Maven模块化管理的继承和聚合机制
父模块设置
上述已经讲过,Maven工具的继承和聚合机制都需要一个父模块,且父模块的打包方式必须为pom,配置示例如下:
<groupId>xxx.yyy</groupId>
<artifactId>xxxParent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<!-- 设置打包方式为 pom -->
<packaging>pom</packaging>
继承机制
Maven继承机制主要解决的是依赖管理,实践如下:
第一步,在父模块中通过dependencyManagement
标签声明需要依赖的jar包以及版本。
<dependencyManagement><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.10.RELEASE</version><scope>test</scope></dependency></dependencies>
</dependencyManagement>
除了可以在父模块中声明需要依赖的jar包及版本外,还可以通过pluginManagement
声明构建时需要的插件及版本:
<build><pluginManagement><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version></plugin></pluginManagement>
</build>
还可以在父模块中实现发布管理,用于配置Maven私服仓库信息:
<distributionManagement><repository><id>private-release</id><url>http://192.168.10.100:9015/repository/private-release/</url></repository><snapshotRepository><id>private-snapshot</id><url>http://192.168.10.100:9015/repository/private-snapshot/</url></snapshotRepository>
</distributionManagement>
私服信息一旦在父模块中声明了,就会全局生效,无需在子模块中再次声明。
第二步,在子模块中声明父模块,并直接引入需要的依赖jar包即可,无需再指定版本。
<!-- <groupId>xxx.yyy</groupId> -->
<artifactId>zzzSubModel</artifactId>
<version>1.0-SNAPSHOT</version><parent><!-- 父模块的坐标与版本 --><groupId>xxx.yyy</groupId><artifactId>xxxParent</artifactId><version>1.0.0-SNAPSHOT</version><!-- 父模块的相对路径 --><relativePath>../pom.xml</relativePath>
</parent>
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId></dependency>
</dependencies>
在子模块中直接引用已经在父模块中声明的构建插件,无需再声明插件版本:
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-deploy-plugin</artifactId></plugin></plugins>
</build>
在子模块中声明父模块时对relativePath
属性的设置说明:
1.relativePath
属性可以省略,前提是父模块已经install
到本地仓库或deploy
到私服,否则子模块无法定位到父模块,不能通过编译
2.相对路径最后可以不写pom.xml
,只定位到父模块的文件夹也可以
聚合机制
继承机制很好地解决了在同一个项目中存在多个模块时的版本依赖管理,但是当多个模块之间存在依赖时,为了方便处理模块之间的依赖更新问题,或者说为了方便在执行Maven构建命令时对所有模块统一进行构建,这时候就需要使用到Maven的聚合机制了。
使用Maven的聚合机制非常简单,只需要在父模块中声明模块信息即可,配置如下:
<modules><module>xxx-yyy-core</module><module>xxx-xxx-model</module><module>xxx-xxx-app</module>
</modules>
在同时使用Maven继承和聚合机制时可能会遇到如下问题:
前面说在子模块中声明父模块时relativePath
属性可以省略,前提是父模块已经构建并install
到本地仓库或deploy
到私服,否则子模块无法构建,但此时父模块同时也聚合了子模块,要构建父模块就又要一起构建子模块,但构建子模块又需要父模块已经构建并install
到本地仓库或deploy
到私服......陷入了套娃问题。
此时构建父模块会报错: Non-resolvable parent POM for XXX.XXX.XXX Could not find artifact com.mzz:parent-maven:pom:1.0-SNAPSHOT and ‘parent.relativePath’ points at wrong local POM
,原因是无法定位父模块。
解决方法也很简单,要么老老实实在子模块中parent
标签中加入relativePath
属性,使Maven可以根据相对路径找到父模块;要么,先将父模块中的modules
注释掉,暂时不做聚合,将父模块install
或deploy
之后再取消注释,然后就能一起构建了。
关于父模块版本的设置
值得注意的是:在子模块中声明父模块时,父模块的的<version>
属性不能直接使用变量。父模块的版本号必须是硬编码的,不能通过变量来动态设置。这是因为父模块的版本号在子模块中引用时,需要是一个固定的值,以便Maven能够正确解析和构建项目。
【参考】
maven 父模块不包含子模块 maven子模块版本继承
Maven高级-详解Maven的继承与聚合
Maven中 <parent > 的<version>可以使用变量吗