【Maven四】——maven聚合和继承

简介: 【Maven四】——maven聚合和继承

前言

由于在具体项目开发过程中对于maven的理解和掌握处于基本运用的阶段,了解maven过于片面,所以本篇博客是博主学习《maven实战》书籍之后对maven聚合和继承的总结,绝大多数内容源于《maven》实战这本书籍。

一、什么是maven的聚合和继承&why

随着技术飞速发展,各类用户对软件的要求越来越高,软件也变得越来越复杂。

软件设计人员往往会采用各种方式对软件划分模块,已得到更加清晰的设计及更高的复用性。


当把Maven应用到实际项目中的时候,也需要将项目分成不同的模块。

Maven的聚合特性能够把项目的各个模块聚合在一起构建,而maven的继承特性则能帮助抽取各个模块相同的依赖和插件等配置。


二、聚合

例如:现在有一个注册服务下面有两个模块,account-email和account-persist。如果我们想要一次构建两个项目(模块),而不是到两个模块的目录下分别执行mvn命令。


为了能够一次构建account-email和account-persist两个模块,我们需要格外的名为account-aggregator的模块,然后通过该模块构建整个项目的所有模块。


account-aggregator作为一个maven项目,它也必须有它自己的POM。

下面是account-aggregator的pom.xml内容:

ca8e05adec1b4f8bb962899b69fc483b.png

上面的POM依旧使用了账户注册服务共同的groupId com.juvenxu.mvnbook.account,artifactId为独立的account-aggregator,版本也与其他两个模块一致。

**但是这里的packaging,值为POM。**而account-email和account-persist的packaging为jar。对于聚合模块来说,其打包方式packaging的值为POM,否则就无法构建。


而modules这是实现聚合的最核心的配置。用户可以通过在一个打包方式为pom的Maven项目中声明任意数量的module元素来实现模块的聚合。


这里每一个module的值都是一个当前POM的相对目录。例如:account-aggregator的POM的路径为D:…\code\account-aggregator\pom.xml那么account-email就对应了目录D:…\code\account-aggregator\account-email\。这两个目录各自包含了pom.xml、src/main/java/、src/test/java等内容。离开account-aggregator也能够独立构建。


一般来说,为了方便快速定位内容,模块所处的目录名称应当与其artifactId一致,不过这不是maven的要求,用户也可以将account-email项目放到email-account/目录下。

这是聚合的配置就需要相应地改成email-account


为了方便用户构建项目、通常将聚合模块放在项目目录的最顶层,其他模块作为聚合模块的子目录存在,这样当用户得到源码的时候,第一眼发现的就是聚合模块的POM不用从多个模块中去寻找聚合模块来构建整个项目。

如图:

0648811fd5cd424b93b18bb876644b35.png

当然也可以是平行的目录结构

385095fb81cd45618f6de3a3ecec7653.png

如果使用平行目录,聚合模块的POM也需要做相应的修改

<modules>
<module>../account-email</module>
</modules>

从聚合模块运行mvn clean install命令得到的输出

6c485a96610f4398930aa5fd62a8330c.png

maven首先解析聚合模块的POM,分析要构建的模块、并计算出一个反应堆构建顺序,探后根据这个顺序依次构建各个模块。


三、继承

面向对象设计中,可以建立一种类的父子结构,然后父类中声明一些字段和方法供子类继承。这样可以做到“一处声明,多处使用”。

类似的在maven世界中,也有类似的机制能让我们抽取出重复的配置。做到“一处声明,多处使用”这就是POM的继承。


我们创建POM的父子结构,然后在父POM中声明一些配置供子POM继承。

继续以账户注册服务为基础。在account-aggregator下创建一个名为account-parent的子目录,然后在该子目录下建立一个所有出account-aggregator之外的模块的父模块。

pom.xml文件如下:

e01551867e004b02b78bf8a2e734a777.png

需要注意的一点,它的packaging为pom,这一点与聚合模块一样,作为父模块的POM,其打包类型也必须为pom,由于父模块只是为了帮助消除配置的重复,因此它本身不包含POM之外的项目文件,也不需要src/maini/java之类的文件夹。


有了父模块,来看看继承父模块的子模块。将account-meail的POM修改如下:

c10b3464039f44b59ebc9a65bc2d8d5b.png

上述POM中使用parent元素声明父模块快,parent下的元素groupId、artifactId和version指定了父模块的坐标,元素relativePath表示父模块POM的相对路径,该例中的…/account-parent/pom.xml表示父POM的位置在与account-email/目录平行的account-parent/目录下。

当项目构建时,maven会首先更具relativePath检查父POM,如果找不到,再从本地仓库中查找。relativePath的默认值为…/pom.xml,也就是说,maven默认父POM在上一层目录下。


1.可继承的POM元素

groupId:项目组ID

version:项目版本

description:项目的描述信息

organization:项目的组织信息

inceptionYear:项目的创始年份

url:项目的URL地址

developers:项目的开发者信息

contribution:项目的贡献者信息

distributionManagement:项目的部署配置

issueManagement:项目的缺陷跟踪系统信息

ciManagement:项目的持续继承系统信息

scm:项目的版本控制系统信息

mailingLists:项目的邮件列表信息

properties:自定义的Maven属性

dependencies:项目的依赖配置

dependencyManagement:项目的依赖管理配置

repositories:项目的仓库配置

build:包括项目的硬盘吗目录配置、输出目录配置、插件配置、插件管理配置等。

reporting:包括项目的报告输出目录配置、报告插件配置等。


2.依赖管理

可继承元素列表包含了dependencies元素,说明依赖是会被继承的。在子模块account-email和account-persist同时依赖了org.springframework:spring-core:2.5.6、org.springframework:spring-beans:2.5.6、org.springframework:spring-context:2.5.6和Junit:junit4.7等等,因此可以将这些依赖配置放到父模块account-parent中,两个子模块就能够移除这些依赖,简化配置。


但是这样会存在一些问题,如果将来项目需要加入一个account-util模块,该模块只是提供一些简单的帮助工具,与springframework完全无关,难道也让他依赖spring-core、spring-beans和spring-context吗?


maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。例如:可以在account-parent中加入这样的dependencyManagement配置如:

53f7e85d13514c1995b09811b40a9466.png

首先父POM将springframework和junit依赖的版本以Maven变量的形式提取了出来,不仅消除了一些重复也使得各依赖的版本处于更加明显的位置。

这是使用的dependencyManagement声明的依赖既不会给account-parent引入依赖,也不会给它的子模块引入一依赖。不过这段配置是会被继承的。


现在修改account-email的POM如:


a1de26bf0cfd4a729922dae7f42f4a53.png

上述的POM中的依赖配置较原来简单了一些,所有的springframework依赖只配置了groupId和artifactId,省去了version,而Junit依赖不仅省去了version,还省去了依赖范围scope。这些信息可以省略是因为account-email继承了account-parent中的dependencyManagement配置。完整的依赖声明已经包含在父POM中,子模块只需要配置简单的groupId和artifactId就能获得对应的依赖信息,从而引入正确的依赖。


使用这种依赖管理机制似乎不能减少太多的POM配置,但强烈推荐采用这种方法。

主要原因是在于父POM中声明之后,子模块在使用依赖的使用就无须声明版本,也就不会发生多个子模块使用依赖版本不一致的情况。这可以降低依赖冲突的几率。

如果子模块不生命依赖的使用,即使依赖已经在父POM的dependencyManagement中声明了,也不会产生任何实际的效果。


补充依赖范围为import的依赖范围,该依赖范围只在dependencyManagement元素下才有效果,使用该范围的依赖通常指向一个POM,作用是将目标POM的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。例如想要在另一个模块中使用

与上面dependencyManagement的一样的配置,除了赋值配置或者继承这两种方式之外,还可以使用import范围依赖,将这一配置导入。如:

a50bd3d281bb4f9a9559cbfb0b386e83.png


3.插件管理

maven也提供了pluginManagement元素帮助管理插件。该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。


例如:

配置了maven-source-plugin,将其jar-no-fork目标绑定到了verity生命周期阶段,以生成项目源码包。如果一个项目中有很多子模块,并且需要得到所有这些模块的源码包,那么更好的方法是父POM中使用pluginManagement配置插件。

如:

82766ecec6d74395ac3411d4872748c7.png

当子模块需要生成源码包的时候,只需要如下配置:

43dd1d08f0b54de3b115ff77181a228e.png

子模块声明使用了maven-source-plugin插件,同时又继承了父模块的pluginManagement配置。如果子模块不需要使用父模块中pluginManagement,可以尽管将其忽略,如果子模块需要不同的插件配置,则可以执行配置以覆盖父模块的pluginManagement配置。


四、聚合与继承的关系

多模块maven项目中聚合与继承其实是两个概念,其目的完全不同。前者主要是为了方便快速构建项目,后者主要是为了消除重复配置。


对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。

对于继承关系的父POM来说,他不知道有哪些子模块继承于它,但那些子模块都必须知道自己的父POM是什么。


共同点:

聚合POM与继承关系的父POM的packaging都必须是pom,同时聚合模块与继承关系的父模块除了POM之外都没有实际内容。

ca0667225f2847a685c923785d4040fa.png


在现有的实际项目中,往往一个POM即是聚合POM,又是父POM。


五、约定优于配置

标准的重要性已经不用过于强调,想象一下,如果不是所有程序员都是基于HTTP协议开发Web应用,互联网会乱成什么样。各个版本的IE,FireFox等浏览器之间的差异已经让很多开发者头疼不已。

java成功的重要原因之一就是它能够屏蔽大部分操作系统的差异,XML流行的原因之一是所有语言都接受它。而maven提倡的“约定优于配置”这是maven最为核心的设计理念之一。

那为什么要使用约定而不是自己更灵活的配置呢?原因之一是使用约定可以大量减少配置。


六、反应堆

在一个多模块的Maven项目中,反应堆(Reactor)是指所有模块组成的一个构件结构。对于单模块的项目,反应堆就是该模块本身。对于多模块项目来说,反应堆包含了模块之间的依赖和继承关系,从而能够自动计算出合理的模块构件顺序。


1.反应堆的构件顺序

如:account-aggregator的聚合配置如下:

5d8260f75fea4483a3dd3c288bdd4a4e.png

构建完成之后会有这样的输出:

b4a1d5d336db436b8337a9196ba048de.png


可以看出反应堆的构建顺序,依次为account-aggregator、account-parent、account-email、account-persist。

19d2342f62af4b8f88e202c74843fc77.png

从上至下的箭头表示POM的读取次序,但这不足以决定反应堆的构建顺序,maven还需要考虑模块之间的继承和依赖关系。图中有向虚线表示模块之间的继承或者依赖关系。上面account-email、account-persit依赖与account-parent,那么account-parent就必须先于另外两个模块构建。


实际的构建顺序是:

maven按顺序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块,如果该依赖模块还依赖于其他模块,则进一步限购键依赖的依赖。

模块间的依赖关系会将反应堆构成一个有向非循环图,各个模块是该图的节点,依赖关系构成有向边。这个图不允许出现循环,因此,当出现模块A依赖与B,而B依赖于A的情况时,maven会报错。


七、裁剪反应堆

有时会需要仅仅构建完整反应堆中的某些个模块,maven提供很多的命令行选项支持裁剪反应堆,输出mvn-h可以看到这些选项。


总结

清楚了maven项目的聚合和继承之后,完全能够搭建起多模块的Maven项目与简洁的管理maven的依赖。


目录
相关文章
|
7月前
|
Java Maven Spring
Maven高级-继承、实施步骤及聚合与继承的区别
Maven高级-继承、实施步骤及聚合与继承的区别
75 0
|
4月前
|
XML Java Maven
"Maven项目模块化大揭秘!掌握Model间最佳继承设计,让你的代码优雅如诗,项目维护不再愁!"
【8月更文挑战第11天】Maven是Java项目中常用的构建工具,其模块化特性对大型项目的管理至关重要。本文介绍Maven中的继承与聚合机制,指导如何通过继承消除重复配置,以及如何通过聚合统一构建多个模块。遵循单一职责原则,文章建议按功能划分模块,并提供了父POM与子POM的配置示例。此外,还讨论了适度模块化、依赖管理的原则,帮助提升项目的可维护性和扩展性。
59 4
|
7月前
|
Java 项目管理 Maven
【揭秘】Maven聚合与继承:如何轻松实现项目依赖管理?
Maven的聚合和继承是Java开发中重要的概念。聚合允许将多个项目组合成一个构建单元,简化多模块项目的构建过程,提高构建效率。继承则让子项目重用父项目的配置和属性,避免了重复定义,增强了项目的一致性和可维护性。通过聚合和继承,Maven为多模块项目的构建和管理提供了高效且灵活的支持,减少了配置冗余,提升了开发效率。
144 0
【揭秘】Maven聚合与继承:如何轻松实现项目依赖管理?
|
7月前
|
Java Maven
Maven【4】(继承)(命令行操作)
Maven【4】(继承)(命令行操作)
46 1
|
7月前
|
Java Maven 开发者
Maven高级-聚合与继承 私服(图文并茂)
Maven高级-聚合与继承 私服(图文并茂)
75 0
|
7月前
|
Java Maven
maven子模块无法继承父模块的jar包解决方案
maven子模块无法继承父模块的jar包解决方案
|
7月前
|
Java Maven
maven的聚合和继承简析
maven的聚合和继承简析
97 0
|
Java Maven Spring
Maven - 父工程的使用与聚合
Maven - 父工程的使用与聚合
176 2
|
7月前
|
Java Maven
Maven高级-聚合及实施步骤
Maven高级-聚合及实施步骤
72 0
|
7月前
|
前端开发 Java Maven
Maven 插件统一修改聚合工程项目版本号
Maven 插件统一修改聚合工程项目版本号