【Maven】(四)图解Maven3依赖的功能特性:依赖范围、依赖传递、依赖冲突

news2024/7/6 19:03:49

文章目录

  • 1.前言
  • 2.pom中的依赖配置
    • 2.1.依赖的概念
    • 2.2.依赖传递
    • 2.3.可选依赖 [optional]
    • 2.4.依赖范围 [scope]
      • 2.4.1.scope的分类
      • 2.4.2.依赖范围对依赖传递的影响
    • 2.5.依赖冲突
      • 2.5.1.直接依赖
      • 2.5.2.间接依赖
    • 2.6.依赖排除 [exclusions]
  • 3.总结

1.前言

本系列文章记录了 Maven 从0开始到实战的过程,Maven 系列历史文章清单:

(一)5分钟做完 Maven 的安装与配置
(二)使用 Maven 创建并运行项目、聊聊 POM 中的坐标与版本号的规则
(三)Maven仓库概念及私服安装与使用 附:Nexus安装包下载地址

有了前面3篇的基础,我们已经清楚了Maven的基础使用方式,在第二篇文章中,简要的介绍了Maven的坐标与版本号规则,那么本篇呢,会在坐标的基础上进行拓展,讲解其他的标签元素,主要内容包括:

  • 依赖传递特性以及如何禁止构件的依赖传递
  • 依赖范围的概念以及选择
  • 在引入相同构件上的不同版本造成依赖冲突
  • 产生依赖冲突时的依赖调解机制
  • 如何手动排除依赖

本篇内容基于Maven的3.5版本

2.pom中的依赖配置

我们先看一下一个完整的依赖配置应该是什么样子的:

<dependencies>
    <dependency>
        <groupId></groupId>
        <artifactId></artifactId>
        <version></version>
        <type></type>
        <scope></scope>
        <optional></optional>
        <exclusions>
            <exclusion>
                <groupId></groupId>
                <artifactId></artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

看起来还是有点复杂的,不过没关系,在下面的内容中会一一解释并使用这些标签。

2.1.依赖的概念

在展开本篇的内容之前,先解释一下什么是依赖,依赖在pom.xml中可以通过dependency引入,假设现在有 A、B、C 三个构件,如下图来表示:
在这里插入图片描述
我们一般会将这图上的关系描述为,A 依赖 B,A 依赖 C,或者B 是 A 的依赖,C 是 A 的依赖,在这基础上,加上自定义的groupId,于是在A构件的pom.xml文件中,就可以写成这样:

<dependencies>
    <dependency>
        <groupId>com.ls.mavendemo</groupId>
        <artifactId>B</artifactId>
        <version>1.0.0</version>
        <type>jar</type>
    </dependency>
    <dependency>
       <groupId>com.ls.mavendemo</groupId>
       <artifactId>C</artifactId>
       <version>1.0.0</version>
    </dependency>
</dependencies>

我们可以看到,对B的依赖中写了一个 <type>jar</type> ,而 C 的没有写,type 指的的当前引入的构件类型,我们在实际开发中往往会忽略这个标签,不写的话,默认就是 jar,这里引入的 B 和 C 都是 jar 包。

2.2.依赖传递

依赖传递是Maven中的一个功能特性,简单的说就是底层的依赖会向上传递,依赖传递的好处在于,我们在引入一个三方构建的时候,这个三方构建可以自动的将它所依赖的构建引入到项目中来,而不需要我们再手动去搜索和手动导入。

用 A,B,C 表示构件,则依赖传递如下图所示:
在这里插入图片描述
此时,A 虽然没有直接依赖 C,但因为依赖传递的特性,A 依然可以使用 C 里面打包的代码(也不是绝对的,依赖传递还会受到依赖范围的影响,详见下面的2.4依赖范围)。

同样的,通过pom.xml 分别描述 A、B 构建的依赖关系:

<!-- A 依赖 B -->
<dependencies>
    <dependency>
       <groupId>com.ls.mavendemo</groupId>
       <artifactId>B</artifactId>
       <version>1.0.0</version>
    </dependency>
</dependencies>
<!-- B 依赖 C -->
<dependencies>
    <dependency>
       <groupId>com.ls.mavendemo</groupId>
       <artifactId>C</artifactId>
       <version>1.0.0</version>
    </dependency>
</dependencies>

2.3.可选依赖 [optional]

在上面的依赖配置基础上,如果我们不想将 C 传递给 A,则可以在 B 引入 C 的时候,将其设置为可选依赖:
在这里插入图片描述
也就是说,引入的依赖如果标记为 optional 则不再向上传递,此时 A 不能再直接使用 C中的代码,可以选依赖在 pom.xml 中表示为:

<!-- B 依赖 C -->
<dependencies>
    <dependency>
       <groupId>com.ls.mavendemo</groupId>
       <artifactId>C</artifactId>
       <version>1.0.0</version>
       <optional>true</optional>
    </dependency>
</dependencies>

这里再提一句为什么会需要禁止依赖传递,举个简单的开发例子,我们在一个 WEB 项目的开发中,将 service 层与 dao 分别拆成了两个构件,service 包依赖 dao 包,但是 service 中只需要使用我们写的主代码,而不需要使用例如数据库驱动JDBCORM框架等依赖,我们就可以在 dao 中使用<optional>禁止这些持久化相关的包向上传递到service

2.4.依赖范围 [scope]

Maven提供了3个 classpath 来表示不同的范围,分别是:编译、运行、测试,依赖范围的含义就是,当前引入的依赖,会被分配到哪一个(或多个)范围中使用。

2.4.1.scope的分类

依赖范围在 pom.xml 中使用 <scope></scope> 来表示,有5种选项:

  • compile:默认选项,也是最常用的选项,在编译、运行、测试的classpath中有引入
  • provided:只在编译、测试引入,运行时不引入,例如:lombok
  • runtime:只在运行、测试引入,编译期不引入,例如:mysql-connector-java
  • test:只在测试中有效,例如:JUnit
  • system:与provided相同,但是需要手动指定依赖文件路径

不考虑system(system会破坏可移植性,不推荐使用,我们直接忘掉它)的关系如下图所示:
在这里插入图片描述
下面是针对几种不同的scope的示例:

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.9</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
        <scope>runtime</scope>
    </dependency>
<dependencies>

2.4.2.依赖范围对依赖传递的影响

在上面的内容中提到了,如果不想将 C 传递到 A 可以使用 optional 的方式标记,除此之外,scope 的配置也对依赖传递有影响。

官网中对 scope 的描述如下:
在这里插入图片描述
可以看到红框中的部分,这里提到了 compile 可以直接向上传递,providedtest 不能向上传递,对 runtime 没有描述,这里我们可以创建3个 Maven 项目验证一下,如下:

在这里插入图片描述

上面的代码放到了gitee中:《代码地址下载》,下载后先在dependecy-demo 目录下运行 mvn clean install

这里创建了3个 Maven 项目,其中 demo-a 依赖 demo-bdemo-b 依赖 demo-c,并在插件中心中下载了一个分析 Maven 依赖的插件 Maven-Helper,然后按照下面的顺序依次进行验证。

  1. demo-b 中对 demo-c 的依赖 scope 修改为 compile
    <dependency>
        <groupId>com.ls.maven</groupId>
        <artifactId>demo-c</artifactId>
        <version>1.0.0</version>
        <scope>compile</scope>
    </dependency>
    
  2. 依次将 demo-a 中对 demo-b 的依赖scope 修改为 compileruntimeprovidedtest
    <dependency>
        <groupId>com.ls.maven</groupId>
        <artifactId>demo-b</artifactId>
        <version>1.0.0</version>
        <scope>compile</scope>
    </dependency>
    
  3. 每修改一次,打开 demo-apom.xml ,找到 Maven-helper 插件进行分析。
    在这里插入图片描述
  4. demo-b 中对 demo-c 的依赖 scope 修改为 runtime 重复上面的3个步骤,直到将 compileruntimeprovidedtest 这4个范围都验证完成

下表中的纵向为 demo-a 中的scope,横向为 demo-b 中的 scope,中间的部分为 demo-c 传递到demo-a的scope,经过组合之后,一共得到了16个结果:

scopecompileruntimeprovidedtest
compilecompileruntime--
runtimeruntimeruntime--
providedprovidedprovided--
testtesttest--

我们把 demo-a 依赖 demo-b 称为直接依赖,demo-a 依赖 demo-c 称为间接依赖,来得出的最终结论为:

  • providedtest 范围不会向上传递。
  • compile 范围在向上传递的时候,间接依赖于直接依赖的范围一致。
  • runtime 范围与 compile 类似,区别在于直接依赖为compile时,间接依赖的范围依然是runtime

2.5.依赖冲突

不管是直接依赖,还是间接依赖,只要引入不同verison的相同构件,就会出现依赖冲突,针对依赖冲突,Maven自带了依赖调解机制,下面用两个例子来说明依赖调解。

2.5.1.直接依赖

先看直接依赖的例子,当A的 pom.xml 中引入了 B 的两个版本,此时哪个版本会生效呢?
在这里插入图片描述

<dependency>
    <groupId>com.ls.maven</groupId>
    <artifactId>demo-b</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>com.ls.maven</groupId>
    <artifactId>demo-b</artifactId>
    <version>1.0.1</version>
</dependency>

在这里插入图片描述

查看依赖分析后发现1.0.1生效了,也就是说,后声明的依赖会覆盖先声明的依赖。

2.5.2.间接依赖

看下面这张图,依赖的路径上存在间接依赖,这种情况生效的是哪个版本呢?
在这里插入图片描述
猜也能猜的到,生效的肯定是1.0.1,下面就来验证一下我们的猜测:

  1. 在A中引入B、和C的1.0.1版本
    <dependency>
        <groupId>com.ls.maven</groupId>
        <artifactId>demo-b</artifactId>
        <version>1.0.1</version>
    </dependency>
    <dependency>
        <groupId>com.ls.maven</groupId>
        <artifactId>demo-c</artifactId>
        <version>1.0.1</version>
    </dependency>
    
  2. 在B中引入C的1.0.0版本
    <dependency>
        <groupId>com.ls.maven</groupId>
        <artifactId>demo-c</artifactId>
        <version>1.0.0</version>
    </dependency>
    

回到A的pom.xml中看一下依赖分析:
在这里插入图片描述
可以看到红色的字体,意思是:

从B中间接依赖的C的1.0.0 版本,由于与1.0.1版本冲突,所以被省略掉了

也就是说,依赖路径最短的那个依赖会生效,也就是就近原则


有了这个基础之后,我们再看一个更加复杂的路径,你能判断出D在中的生效的是哪个版本吗?
在这里插入图片描述

2.6.依赖排除 [exclusions]

在开发的过程中,有时候我们需要手动的排除一部分传递性的依赖,然后再定义我们需要的依赖,举个简单的例子:

我们在 spring-boot 的项目中使用 redis,首先需要引入一个 starter,这个 starter 中又依赖了 lettuce 这个客户端:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

在这里插入图片描述
此时,我们不想使用这个客户端,想切换成 jedis,那么我们就可以使用exclusionslettuce 排除掉。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.2.5.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

然后再引入 jedis 的依赖就可以了。


注:这里的exclusion 中不需要使用版本号。

3.总结

在本篇中,我们了解了依赖的相关功能特性:

  • 依赖范围:不同的scope对应不同的classpath
    • compile:默认选项,也是最常用的选项,在编译、运行、测试的classpath中有引入
    • provided:只在编译、测试引入,运行时不引入,例如:lombok
    • runtime:只在运行、测试引入,编译期不引入,例如:mysql-connector-java
    • test:只在测试中有效,例如:JUnit
  • 依赖传递:在特定的依赖范围下,依赖会向上传递
    • providedtest 范围不会向上传递
    • compile 范围在向上传递的时候,间接依赖于直接依赖的范围一致
    • runtime 范围与 compile 类似,区别在于直接依赖为compile时,间接依赖的范围依然是runtime
  • 可选依赖:使用 optional 可以禁止依赖向上传递
  • 依赖冲突:引入不同版本的相同构件会发生依赖冲突,Maven自带依赖调解机制
    • 就近原则:依赖最近中,距离当前项目路径最短的版本会被引入
    • 覆盖原则:依赖路径相同时,后定义的依赖版本会覆盖先定义的依赖版本
  • 依赖排除:可以手动使用exclusions排除依赖构件中的间接依赖

依赖相关的内容就结束了,下一篇会讲解 Maven 模块的聚合与继承,并提供一个 Maven 多模块构件的最佳实践。


如果觉得本文有所帮助的话,可以帮忙点点赞哦~!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搭建XXL-JOB

搭建XXL-JOB 1、 调度中心 首先下载XXL-JOB GitHub&#xff1a;GitHub - xuxueli/xxl-job: A distributed task scheduling framework.&#xff08;分布式任务调度平台XXL-JOB&#xff09; 码云&#xff1a;xxl-job: 一个分布式任务调度平台&#xff0c;其核心设计目标是开…

JDK17 下载与安装

JDK是 Java 语言的软件开发工具包&#xff0c;主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心&#xff0c;它包含了JAVA的运行环境&#xff08;JVMJava系统类库&#xff09;和JAVA工具。 1、下载JDK17 1.1、首先&#xff0c;检查下电脑中是否安装jd…

车机系统开发——Android Automotive

Android Automotive介绍 Android Automotive是⼀个基本的Android平台&#xff0c;它运⾏预安装的&#xff08;车载信息娱乐&#xff09;IVI系统&#xff0c;Android应⽤程序以及可选的第⼆⽅和第三⽅Android应⽤程序。 Android Automotive的硬件抽象层(HAL)为Android框架提供…

封装小程序request请求[接口函数]

在这篇小程序API的Promise化文章中讲到小程序官方提供的异步API都是基于回调函数来实现的&#xff0c;在大量的使用这种回调函数就会造成回调地狱的问题&#xff0c;以及代码的可读性和可维护性差&#xff0c;通过对小程序API的Promise化能解决&#xff0c;那么本篇是来讲进行对…

203. 移除链表元素 L2-027 名人堂与代金券

给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5] 示例 2&#xff1a; 输入&#…

内网离线安装docker-ce工具,带你来了解!

虽然通常我们都是使用网络来安装 docker 的&#xff0c;但是对于安全要求比较高的业务或者用户来说&#xff0c;部署产品需要在内网部署&#xff0c;而内网通常是无法访问外部网络的&#xff0c;所以就需要通过内网进行安装了。 1. yum 离线安装包获取方法 下载软件包 这里以…

语义分割数据标注案例分析

语义分割&#xff08;Semantic Segmentation&#xff09;是计算机视觉领域中的一种重要任务&#xff0c;它的目的是将图像中的每个像素分配到对应的语义类别中。简单来说&#xff0c;就是将一张图像分割成多个区域&#xff0c;并为每个像素指定一个标签&#xff0c;标识出它属于…

实时手势识别(C++与python都可实现)

一、前提配置&#xff1a; Windows&#xff0c;visual studio 2019&#xff0c;opencv&#xff0c;python10&#xff0c;opencv-python&#xff0c;numpy&#xff0c;tensorflow&#xff0c;mediapipe&#xff0c;math 1.安装python环境 这里我个人使用的安装python10&#…

MySQL进阶篇之MySQL管理

07、MySQL管理 7.1、系统数据库 MySQL数据库安装完成后&#xff0c;自带了四个数据库&#xff0c;具体作用如下&#xff1a; 数据库含义mysql存储MySQL服务器正常运行所需要的各种信息&#xff08;时区、主从、用户、权限等&#xff09;information_schema提供了访问数据库元…

科技赋能智慧警务,“链上天眼科技助警中国行”在京启动

2月28日&#xff0c;由全球领先的区块链大数据科技企业欧科云链和中国警察网联合举办的“链上天眼科技助警中国行”活动&#xff08;下称“活动”&#xff09;&#xff0c;在北京正式启动。为了普及区块链基础知识&#xff0c;以及虚拟货币犯罪追踪与打击的新型技术应用经验&am…

(三)随处可见的LED广告屏是怎么工作的呢?接入GUI

续上文&#xff0c;本篇我们将尝试接入一个GUI来控制点阵屏。在前两篇中&#xff0c;我们相继介绍了点阵屏的控制原理&#xff0c;以及如何让点阵屏按照我们所想的进行显示。本篇将在此基础上接入一个GUI&#xff0c;使点阵屏的控制更加优雅。限于阅读体验和展示效果&#xff0…

王道计算机网络课代表 - 考研计算机 第一章 计算机网络体系结构 究极精华总结笔记

本篇博客是考研期间学习王道课程 传送门 的笔记&#xff0c;以及一整年里对 计算机网络 知识点的理解的总结。希望对新一届的计算机考研人提供帮助&#xff01;&#xff01;&#xff01; 关于对 “计算机网络体系结构” 章节知识点总结的十分全面&#xff0c;涵括了《计算机网络…

SpringMVC之JSON工具:Jackson Gson 和fastjson通过JSON工具来解决文字乱码和时间格式问题——通过JSON工具生成JSON

什么是Json&#xff1f; JSON&#xff1a;JavaScript Object NotationJS对象 它是一种轻量级的数据交换格式JSON&#xff08;当前是交互的顶流&#xff09;&#xff0c;它自身具有独立的编程格式&#xff0c;它的特点是简洁和清晰&#xff0c;Json的存在大大改造了网络传输的…

ffmpeg音视频解码和渲染流程

背景&#xff1a; 随着游戏娱乐等直播业务的增长&#xff0c;在移动端观看直播的需求也日益迫切。但是移动端原生的播放器对各种直播流的支持却不是很好。Android 原生的 MediaPlayer 不支持 flv、hls 直播流&#xff0c;iOS 只支持标准的 HLS 流。本文介绍一种基于 ffplay 框…

挑选销售自动化工具应该关注什么功能?

销售自动化可以极大地提高你的生产力和效率&#xff0c;每周都为你节省时间。这样&#xff0c;你就可以把更多的时间用于完成交易&#xff0c;而减少用于行政任务的时间。市面上的销售自动化工具有很多&#xff0c;作为一般经验法则&#xff0c;以下是销售自动化工具中需要寻找…

智能家居Homekit系列一智能插座

WiFi智能插座对于新手接触智能家居产品更加友好&#xff0c;不需要额外购买网关设备 很多智能小配件也给我们得生活带来极大的便捷&#xff0c;智能插座就是其中之一&#xff0c;比如外出忘记关空调&#xff0c;可以拿起手机远程关闭。 简单说就是&#xff1a;插座可以连接wi…

【微信小程序】-- WXML 模板语法 - 列表渲染 -- wx:for wx:key(十二)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

Web前端学习:四 - 练习

三九–四一&#xff1a;百度页面制作 1、左右居中&#xff1a; text-align: center; 2、去掉li默认的状态 list-style: none; li中有的有点&#xff0c;有的有序&#xff0c;此代码去掉默认状态 3、伪类&#xff1a;hovar 一般显示为color: #0f0e0f&#xff0c; 当鼠标接触时…

【JAVA程序设计】【C00107】基于SSM(非maven)的民宿短租管理系统——有文档

【C00107】基于SSM&#xff08;非maven&#xff09;的民宿短租管理系统——有文档项目简介项目获取开发环境项目技术运行截图项目简介 基于ssm框架非maven开发的民宿短租管理系统分为二种用户&#xff1a;系统管理员、用户 管理员角色包含以下功能&#xff1a; 用户管理、客房…

本地新项目上传到git的详细步骤

前提&#xff1a;你本地的项目目录里要记得添加.gitignore忽略文件&#xff0c;免得把一些无用的文件提交&#xff0c;内容如下&#xff0c;可直接粘贴&#xff1a; # Created by .ignore support plugin (hsz.mobi) ### Java template # Compiled class file *.class# Log fi…