maven聚合和继承

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

随着技术飞速发展,各类用户对软件的要求越来越高,软件也变得越来越复杂。
软件设计人员往往会采用各种方式对软件划分模块,已得到更加清晰的设计及更高的复用性。

当把Maven应用到实际项目中的时候,也需要将项目分成不同的模块。
Maven的聚合特性能够把项目的各个模块聚合在一起构建,而maven的继承特性则能帮助抽取各个模块相同的依赖和插件等配置。

二、聚合

例如:现在有一个商城服务下面有两个模块,item-service和cart-service。如果我们想要一次构建两个项目(模块),而不是到两个模块的目录下分别执行mvn命令。
为了能够一次构建item-service和cart-service两个模块,我们需要格外的名为hmall的模块,然后通过该模块构建整个项目的所有模块。
hmall作为一个maven项目,它也必须有它自己的POM。
下面是hmall的pom.xml内容:
在这里插入图片描述
上面的POM依旧使用了商品服务共同的groupId com.heima,artifactId为独立的hamll,版本也与其他两个模块一致。

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

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

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

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

  • hmall
    • cart-service
      • pom.xml
    • item-service
      • pom.xml
    • pom.xml

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

  • hmall
    • pom.xml
  • cart-service
    • pom.xml
  • item-service
    • pom.xml

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

<modules>
<module>../item-service</module>
</modules>

从聚合模块运行mvn clean install命令得到的输出
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
maven首先解析聚合模块的POM,分析要构建的模块、并计算出一个反应堆构建顺序,探后根据这个顺序依次构建各个模块。

三、继承

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

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

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

如果A工程继承B工程,则代表A工程默认依赖B工程依赖的所有资源,且可以应用B工程中定义的所有资源信息。

被继承的工程(B工程)只能是POM工程。

注意:在父项目中放在中的内容时不被子项目继承,不可以直接使用

放在中的内容主要目的是进行版本管理。里面的内容在子项目中依赖时坐标只需要填写

和即可。(注意:如果子项目不希望使用父项目的版本,可以明确配置version)。

继续以商城服务为基础。pom.xml文件如下:
在这里插入图片描述
需要注意的一点,它的packaging为pom,这一点与聚合模块一样,作为父模块的POM,其打包类型也必须为pom,由于父模块只是为了帮助消除配置的重复,因此它本身不包含POM之外的项目文件,也不需要src/maini/java之类的文件夹。
有了父模块,来看看继承父模块的子模块。将item-service的POM修改如下:
在这里插入图片描述
上述POM中使用parent元素声明父模块快,parent下的元素groupId、artifactId和version指定了父模块的坐标,元素relativePath表示父模块POM的相对路径,该例中的hmall/pom.xml表示父POM的位置在与item-serice/pom.xml上一层的目录下。
当项目构建时,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元素,说明依赖是会被继承的。在子模块item-service和cart-service同时依赖了lombok,spring-boot-starter-test等等,因此可以将这些依赖配置放到父模块hmall中,两个子模块就能够移除这些依赖,简化配置。

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

<?xml version="1.0" encoding="UTF-8"?>
<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.heima</groupId><artifactId>hmall</artifactId><packaging>pom</packaging><version>1.0.0</version><modules><module>hm-common</module><module>item-service</module><module>cart-service</module></modules><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/></parent><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><org.projectlombok.version>1.18.20</org.projectlombok.version><spring-cloud.version>2021.0.3</spring-cloud.version></properties><!-- 对依赖包进行管理 --><dependencyManagement><dependencies><!--spring cloud--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies></dependencyManagement><dependencies><!-- lombok 管理 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${org.projectlombok.version}</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin></plugins></build>
</project>

首先父POM将lombok依赖的版本以Maven变量的形式提取了出来,不仅消除了一些重复也使得各依赖的版本处于更加明显的位置。
这是使用的dependencyManagement声明的依赖既不会给item-service引入依赖,也不会给它的子模块引入一依赖。不过这段配置是会被继承的。
现在修改item-service的POM如:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.heima</groupId><artifactId>hmall</artifactId><version>1.0.0</version></parent><artifactId>item-service</artifactId><dependencies><!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

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

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

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

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

补充依赖范围为import的依赖范围,该依赖范围只在dependencyManagement元素下才有效果,使用该范围的依赖通常指向一个POM,作用是将目标POM的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。例如想要在另一个模块中使用与上面dependencyManagement的一样的配置,除了赋值配置或者继承这两种方式之外,还可以使用import范围依赖,将这一配置导入。如:

<dependencyManagement><dependencies><dependency><groupId>com.heima</groupId><artifactId>hmall</artifactId><version>1.0.0</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

3.插件管理

maven也提供了pluginManagement元素帮助管理插件。该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。
例如:
配置了maven-source-plugin,将其jar-no-fork目标绑定到了verity生命周期阶段,以生成项目源码包。如果一个项目中有很多子模块,并且需要得到所有这些模块的源码包,那么更好的方法是父POM中使用pluginManagement配置插件。
如:

<build><pluginManagement><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>2.2.1</version><executions><execution><id>attach-sources</id><phase>verify</phase><goals><goal>jar-no-fork</goal></goals></execution></executions></plugin></plugins></pluginManagement></build>

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

 <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId></plugin></plugins></build>

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

四、聚合与继承的关系

多模块maven项目中聚合与继承其实是两个概念,其目的完全不同。前者主要是为了方便快速构建项目,后者主要是为了消除重复配置。
对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。
对于继承关系的父POM来说,他不知道有哪些子模块继承于它,但那些子模块都必须知道自己的父POM是什么。
共同点:
聚合POM与继承关系的父POM的packaging都必须是pom,同时聚合模块与继承关系的父模块除了POM之外都没有实际内容。
在现有的实际项目中,往往一个POM即是聚合POM,又是父POM。

五、约定优于配置

标准的重要性已经不用过于强调,想象一下,如果不是所有程序员都是基于HTTP协议开发Web应用,互联网会乱成什么样。各个版本的IE,FireFox等浏览器之间的差异已经让很多开发者头疼不已。
java成功的重要原因之一就是它能够屏蔽大部分操作系统的差异,XML流行的原因之一是所有语言都接受它。而maven提倡的“约定优于配置”这是maven最为核心的设计理念之一。
那为什么要使用约定而不是自己更灵活的配置呢?原因之一是使用约定可以大量减少配置。

六、反应堆

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

1.反应堆的构件顺序

如:hmall的聚合配置如下:
在这里插入图片描述
构建完成之后会有这样的输出:
在这里插入图片描述
可以看出反应堆的构建顺序,依次为hmall、hm-common、item-service、cart-service。
实际的构建顺序是:
maven按顺序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块,如果该依赖模块还依赖于其他模块,则进一步限购键依赖的依赖。
模块间的依赖关系会将反应堆构成一个有向非循环图,各个模块是该图的节点,依赖关系构成有向边。这个图不允许出现循环,因此,当出现模块A依赖与B,而B依赖于A的情况时,maven会报错。

七、裁剪反应堆

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/166541.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用CDN构建读取缓存设计

在构建需要高吞吐量和最小响应时间的系统的API时&#xff0c;缓存几乎是不可避免的。每个在分布式系统上工作的开发人员都曾在某个时候使用过某种缓存机制。在本文中&#xff0c;我们将探讨如何使用CDN构建读取缓存设计&#xff0c;不仅可以优化您的API&#xff0c;还可以降低基…

6.5 Elasticsearch(五)Spring Data Elasticsearch - 增删改查API

文章目录 1.Spring Data Elasticsearch2.案例准备2.1 在 Elasticsearch 中创建 students 索引2.2 案例测试说明 3.创建项目3.1 新建工程3.2 新建 springboot module&#xff0c;添加 spring data elasticsearch 依赖3.3 pom.xml 文件3.4 application.yml 配置 4.Student 实体类…

如何更改eclipse的JDK版本

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、有时候导入一些网上的资源需要更换JDK二、使用步骤1. 总结 一、有时候导入一些网上的资源需要更换JDK 具体操作如下 二、使用步骤 1. 在eclipse上方工具栏找…

当中国走进全球化的“深水区”,亚马逊云科技解码云时代的中国式跃升

中国跨境贸易中支付金融与服务领域的综合创新型企业连连国际的联席CEO沈恩光发现&#xff0c;眼下&#xff0c;很多跨境电商的出海方式已发生了变化。几年前&#xff0c;它们还主要借助第三方电商平台&#xff0c;而现在&#xff0c;更多公司开始选择通过自主渠道进入海外市场&…

Vue 3使用 Iconify 作为图标库与图标离线加载的方法、 Icones 开源在线图标浏览库的使用

之前一直naive-ui搭配使用的是xicons&#xff0c;后来发现Iconify支持的图标合集更多&#xff0c;因此转而使用Iconify。 与FontAwesome不同的是&#xff0c;Iconify配合Icones相当于是一个合集&#xff0c;Iconify提供了快捷引入图标的方式&#xff0c;而Icones是一个大的图标…

Kubeadm部署k8s集群 kuboard

目录 主机准备 主机配置 修改主机名&#xff08;三个节点分别执行&#xff09; 配置hosts&#xff08;所有节点&#xff09; 关闭防火墙、selinux、swap、dnsmasq(所有节点) 安装依赖包&#xff08;所有节点&#xff09; 系统参数设置(所有节点) 时间同步(所有节点) 配…

华为云CodeArts IDE for Java安装使用教程

本篇内容主要介绍使用华为云CodeArts IDE for Java创建工程、代码补全、运行调试代码、Build构建和测试相关的主要功能。 一、下载安装华为云CodeArts IDE for Java 华为云CodeArts IDE for Java安装要求 至少需要 2 GB RAM &#xff0c;但是推荐8 GB RAM; 至少需要 2.5 GB 硬…

【源码】C/C++运动会计分系统 期末设计源码

文章目录 题目介绍功能源码效果展示带报告&#xff08;内容&#xff09; 题目介绍 使用语言&#xff1a; 两个版本都会发&#xff1a; 版本1&#xff1a;C语言 版本2&#xff1a; C 代码量&#xff1a; 500 题目介绍&#xff1a; 要求&#xff1a;初始化输入&#xff1a;N-参赛…

一起学数据结构(10)——排序

从本文开始&#xff0c;通过若干篇文章展开对于数据结构中——排序的介绍。 1. 排序的概念&#xff1a; 将一堆杂乱无章的数据&#xff0c;通过一定的规律顺序排列起来。即将一个无序序列排列成一个有序序&#xff08;由小到大或者由大到小&#xff09;的运算。 在数据的排序中…

【经历】在职8个月->丰富且珍贵

在职8个月->丰富且珍贵 2021-3~2021-11&#xff1a;面试进入一家做400电话的公司&#xff0c;我进入公司时&#xff0c;加上我只有四个人(老板、人事、业务)&#xff0c;开发只有我&#xff0c;所以&#xff1a;产品~设计~前端~后端~测试~上线~维护~培训&#xff0c;只有我自…

【项目实战】从零开始设计并实现一个接口异常链路分析器

这不是马上要到1024了吗&#xff0c;这不得弄个什么工具给部门项目提提效&#x1f62f;&#xff1f; 1. 背景 在我们服务端应用当中&#xff0c;我们往往会要求更高的性能和更高的稳定性&#xff0c;但实际开发的过程中&#xff0c;可能会出现很多赶时间的情况&#xff08;也…

jsp内的${}循环一次及循环几次相加出总和

目录 表内读数据循环一次的相加显示&#xff1a; 表内读数据循环几次的相加&#xff0c;计算出总和并显示&#xff1a; 表内读数据循环一次的相加显示&#xff1a; <c:forEach items"${sessionScope.PropertyFeelist}" var"pf"><h5> ${pf.w…

C++ Primer笔记002:引用/指针/const

文章目录 1. 引用1.1 引用不是对象或变量1.2 引用必须初始化1.3 不能定义引用的引用1.4 引用类型要适配1.5 非const引用不能绑定字面值 2. 指针2.1 指针和引用的区别2.2 指针的指针2.3 类型一致2.4 指针的引用2.5 void 型指针 3. const3.1 const的基本作用3.2 对const变量的引用…

Jprofiler V14中文使用文档

JProfiler介绍 什么是JProfiler? JProfiler是一个用于分析运行JVM内部情况的专业工具。 在开发中你可以使用它,用于质量保证,也可以解决你的生产系统遇到的问题。 JProfiler处理四个主要问题: 方法调用 这通常被称为"CPU分析"。方法调用可以通过不同的方式进行测…

抖音热搜榜:探索热门话题的奥秘

抖音热搜榜是抖音平台根据用户观看、点赞、评论、分享等行为数据&#xff0c;综合计算得出的热门话题排行榜。它反映了当前平台上最热门、最受欢迎的话题和内容。抖音热搜榜有以下几个作用和意义&#xff1a; 1. 满足用户需求&#xff1a;抖音热搜榜为用户提供了丰富的热门话题…

微信小程序完整项目实战(前端+后端)

基于微信小程序的在线商城点单系统 前言&#xff1a;闲来无事&#xff0c;想以后自己开一个小超市或者小吃店&#xff0c;能够支持线上下单&#xff0c;既方便客户也方便自己。系统采用Java语言作为后端实现与小程序的交互&#xff0c;给用来学习或者想自己开个小店的朋友当个参…

Reparameterization trick(重参数化技巧)

“Reparameterization trick”&#xff08;重参数化技巧&#xff09;是一种在训练生成模型中处理随机性潜在变量的方法&#xff0c;特别常见于变分自动编码器&#xff08;VAE&#xff09;等模型中。这个技巧的目的是使模型可微分&#xff08;differentiable&#xff09;&#x…

新年学新语言Go之五

一、前言 Go虽然不算是面向对象语言&#xff0c;但它支持面向对象一些特性&#xff0c;面向接口编程是Go一个很重要的特性&#xff0c;而Go的接口与Java的接口区别很大&#xff0c;Go的接口比较复杂&#xff0c;这里仅用一个最简单例子做介绍&#xff0c;复杂的我也还没学。 …

PostgreSQL与MySQL数据库对比:适用场景和选择指南

数据库是现代应用程序的基石之一&#xff0c;而在选择合适的数据库管理系统&#xff08;DBMS&#xff09;时&#xff0c;开发者常常会面临着许多选择。在这方面&#xff0c;PostgreSQL和MySQL是两个备受瞩目的选项。本文将深入研究这两者之间的异同&#xff0c;并为您提供适用场…

鸿蒙HarmonyOS应用开发:扫描仪文件扫描

华为鸿蒙HarmonyOS已经发展到4.0&#xff0c;使用ArkTS作为开发语言。这篇文章结合Dynamsoft Service开发一个简单的鸿蒙应用&#xff0c;用来获取办公室里连接PC的扫描仪(惠普&#xff0c;富士通&#xff0c;爱普生&#xff0c;等)&#xff0c;把文档扫描到手机里。 准备工作…