maven|Maven---彻底理解聚合项目和父子模块的关系

实际的开发项目中,经常会使用maven搭建聚合项目或父子模块,比如说现在的spring cloud微服务项目,需要把API接口和具体的业务逻辑分开,API可以打成jar包给其它模块项目用feign调用。另外例如一些SpringFramework框架,它的项目结构就是聚合多模块结构;我们使用的Spring-Boot项目中也算是一种父子模块结构。所以了解这部分知识对我们将来看一个陌生项目的结构还是很有帮助的。
这篇博客就介绍Maven关于聚合项目和父子模块的相关知识,聚合项目和父子项目没有什么联系,这里我们分别来介绍它们。
聚合项目 聚合项目又称为多模块项目,这种结构的目的是为了统一构建项目,也就是说我对根项目的任何mvn 命令操作,都会相应的执行到每一个被聚合的module项目中。想象一下,如果你创建了10个项目,如果你要对这10个项目进行 mvn install操作(将项目发布到本地仓库),难道要单独的进行10遍操作么?
实际点,我们来看一看像spring-framework这种框架内维护的项目内部是什么样子。
maven|Maven---彻底理解聚合项目和父子模块的关系
文章图片

内部大概是22个子项目,如果每一个都要手动去执行mvn命令,那真的也是无聊的操作了。看一看它的根项目中的setting.gradle中的聚合内容。(spring-framework默认gradle构建,不用担心,把它当作maven看待即可。)
maven|Maven---彻底理解聚合项目和父子模块的关系
文章图片

这里的include就相当于maven中的标签。
所以为了解决这种耗时又无聊的操作,项目构建工具如maven提供的聚合特性的好处就来了,只需要对根项目操作一次命令即可。
有一点要清楚,聚合仅仅是为了方便我们管理多个项目,提高效率。它和父子模块没有半点关系,被聚合的模块基本感知不到根模块的存在
搭建聚合项目
下面我们来动手搭建聚合项目。

  1. 首先我们先创建一个根项目,来作为模块的管理者。
    由于根项目仅仅是为了聚合其它的项目,所以它不需要java代码,也就不需要src目录,删掉即可。pom.xml中也没有任何的依赖。
maven|Maven---彻底理解聚合项目和父子模块的关系
文章图片

2. 添加子模块。
我用的是idea,所以右键添加module即可。
maven|Maven---彻底理解聚合项目和父子模块的关系
文章图片

这里我创建了两个模块,当前整体模块结构如下。
maven|Maven---彻底理解聚合项目和父子模块的关系
文章图片

聚合项目,根模块的打包方式必须为pom。
通过 标签指定被聚合的模块。
同时创建的其它模块默认在maven-demo项目目录下一级创建,实际上项目目录层级没有要求,如果项目层级不同,则需要修改module标签内的相对路径,这里我们手动将second-module项目移到first-demo项目目录下看一看。
在单纯的聚合项目中,被聚合的项目是没有上下级或者说父子级区别的,都是相同的级别。
maven|Maven---彻底理解聚合项目和父子模块的关系
文章图片

3. 现在发布所有模块到本地仓库,只需要在maven-demo下,执行一次 mvn install 命令即可。
注意执行命令的目录。
H:\IdeaProjects\maven-demo>mvn install

可以看到所有的聚合模块都会执行。
[INFO] first-moudle ....................................... SUCCESS [1.505 s] [INFO] second-moudle ...................................... SUCCESS [0.067 s] [INFO] mavendemo .......................................... SUCCESS [1.438 s] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.116 s [INFO] Finished at: 2021-02-07T22:51:10+08:00 [INFO] Final Memory: 16M/168M [INFO] ------------------------------------------------------------------------

父子模块 父子模块这种项目结构,本质上就是继承关系。上边我们说过聚合模块结构没有上下级区分,但这里的父子模块就要区分上下级了(这里的上下级不是指文件目录的上下级),子模块会继承父模块的相关依赖配置。
继承在java里的好处就是子类可以使用父类的属性和方法,减少代码冗余。
maven这里同样也是这个好处:
  1. 父模块的groupId和version会传递到子模块,父子模块作为一个整体,子模块不需要再声明,仅需要指定自己独有的artifactId即可,当然如果你依赖的父模块和你的项目不是同一个groupId时,你也可以指定子模块的groupId和version;
  2. 父模块的依赖配置会传递到子模块,子模块不需要再单独引入依赖;
  3. 父模块可以管理依赖的版本,子模块可以自由灵活的选择需要的依赖,不需要再关心版本的问题。
之前我们在聚合项目那里说过,被聚合的模块根本无法感知到根模块的存在。父子模块正好相反,父模块根本无法感知到哪个子模块把它当作爸爸。
这也是为什么实际项目中,我们在父模块中聚合子模块,同时子模块中又继承父模块。我们作为一个第三者局外人,得清楚它们之间的关系,方便我们统一管理。当然你使用ide创建父子模块时,默认就会保持这种关系(既是聚合项目关系,又是父子模块关系)。
搭建父子模块
这里我们搭建一个单纯的父子模块,也就是不在父模块中配置module标签,仅在子模块中指定它的父模块是谁。父亲不知道儿子是谁,儿子却知道自己的爹在哪里。
1.创建父模块
父子模块中,父模块一般作为子模块依赖的管理,它只需要定义好依赖的版本和必须的依赖即可。
父模块的打包方式必须为pom。
pom.xml
4.0.0com.binghuazheng.maven mavendemo pom 1.0.0-SNAPSHOT3.8 org.apache.commons commons-lang3 ${commons.lang3.version} junit junit 4.12 org.apache.maven.plugins maven-compiler-plugin >1.8 1.8

父模块添加了junit依赖,指定了commons-lang3依赖的版本,并没有指定聚合的模块。
2.创建子模块
子模块需要通过 标签指定父模块的坐标。然后自由选择需要的依赖,如果此依赖在父模块已指定版本,则子模块不需要再管理版本。
maven|Maven---彻底理解聚合项目和父子模块的关系
文章图片

这里就可以引入父模块的junit。并且根据父模块的commons-lang3版本,直接依赖即可。
总结 在我们实际开发中,一般都是将聚合和父子这两种关系混合使用,这里我拆分说,只是为了分别去理解它们各自的作用。
不论父子模块还是聚合模块,根模块的打包方式都必须是pom,下面的模块可以是jar或者war这两种打包方式。
聚合模块这种项目结构,仅仅是为了方便统一管理操作所有的模块。根模块和它内部 标签内的模块是一个整体,项目目录层级上可以不要求一定上下级,但必须保持一定的层级联系。你不能用自己创建的项目去管理操作spring的项目。
父子模块这种项目结构,仅仅是为了方便子模块对依赖的管理,子模块通过 标签,引入父模块的配置去约束子模块的依赖版本。也可以抽出共同的依赖给到父模块,子模块去继承它,减少每个子模块冗余的配置。项目层级没有要求,你可以引入任意的依赖当作父模块,比如spring-boot-starter。
【maven|Maven---彻底理解聚合项目和父子模块的关系】当然混合使用才能发挥它们最大的优势!

    推荐阅读