聊聊Maven的依赖传递、依赖管理、依赖作用域
1. 依赖传递
在Maven中,依赖是会传递的,假如在业务项目中引入了spring-boot-starter-web依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.4</version>
</dependency>
那么业务项目不仅直接引入了spring-boot-starter-web依赖,还间接引入了spring-boot-starter-web的依赖项:
spring-boot-starter、spring-boot-starter-json、spring-boot-starter-tomcat、spring-web、spring-webmvc。
Maven依赖关系如下图所示:

外部库如下图所示:

其中,业务项目对spring-boot-starter-web的依赖称为直接依赖,对spring-boot-starter-web的依赖项:
spring-boot-starter、spring-boot-starter-json、spring-boot-starter-tomcat、spring-web、spring-webmvc
的依赖称为间接依赖。

2. 依赖管理
dependencyManagement元素主要用来统一管理依赖项的版本号。
假如父项目的pom文件中声明了如下依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
</dependencies>
</dependencyManagement>
那么子项目在添加该依赖时,可以不指定版本号:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
</dependencies>
Maven会自动找到父项目中声明的该依赖项的版本号,如下图所示:


这样的优点是可以统一在父项目中管理依赖项的版本号,如果需要升级版本,只需改动父项目一个地方即可,子项目不用改动。
说明:
1)dependencyManagement只是声明依赖项,并没引入依赖项,子项目仍需显式引入,不过可以不指定版本号
2)如果子项目不想使用继承的父项目中的版本号,在子项目中指定版本号即可
3. 依赖作用域
在Maven中,可以使用scope来指定当前依赖项的作用域,常见的值有:compile、provided、runtime、test、import等,如下所示:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
3.1 compile
compile是默认的作用域,如果引入依赖时,没有明确指定作用域,则依赖作用域为compile。
作用域为compile的依赖,在编译、测试和运行时都是可用的,并且会参与项目的打包过程,该依赖会传递给依赖该模块的其他模块。
3.2 provided
作用域为provided的依赖,在编译和测试时是可用的,在运行时是不可用的,不会参与项目的打包过程,也不会传递给其他模块。
比如lombok依赖会在编译时生成相应的get、set等方法,在运行时就不需要这个依赖了,因此常常被指定为provided:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
因为被指定为provided,项目打包时是不包含lombok依赖项的,如下图所示:

如果将上面的代码<scope>provided</scope>删除的话,运行时是下图这样的:

以上验证需将项目打包方式改为war,打包后查看WEB-INF/lib目录
3.3 runtime
作用域为runtime的依赖,在测试和运行时是可用的,在编译时是不可用的,会参与项目的打包过程,也会传递给依赖该模块的
其他模块。
说明:
作用域为runtime的依赖中的类,在项目代码里不能直接用,用了无法通过编译(这里指的是在src/main/java下使用)。
以mysql-connector-java为例,假如引入依赖时是下面这样的:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
下面的示例代码是可以编译通过的:

如果将作用域修改为runtime,上面的示例代码无法通过编译:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
<scope>runtime</scope>
</dependency>

3.4 test
作用域为test的依赖,只在测试时可用(包括测试代码的编译、执行),不会参与项目的打包过程,也不会传递给其他模块。
常见的有junit、mockito等:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
因为被指定为test,项目打包时是不包含junit依赖项的,如下图所示:

如果将上面的代码<scope>test</scope>删除的话,运行时是下图这样的:

以上验证需将项目打包方式改为war,打包后查看WEB-INF/lib目录
说明:
作用域为test的依赖中的类或者注解只能在src/test/java下才可以使用,在src/main/java下无法使用,如junit包下的@Test注解和org.junit.Assert断言类。
3.5 import
每个项目,一般都会继承自一个父项目,在实际的工作中,这个父项目一般都是公司架构组提供的带有公司特色的一个基础项目,
当然也可以是spring boot官方的项目。
以spring boot官方的项目为例:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
</parent>
这个父项目中,会使用dependencyManagement标签对依赖项的版本统一管理,子项目中,可以按需引入父项目
dependencyManagement中定义的依赖,但可以不指定版本号(版本号会自动继承父项目中定义的版本号)。
但是存在以下2个问题:
- Maven是单继承的,一个项目只能有一个parent项目
- parent项目dependencyManagement中的依赖项会越来越多,不好管理
依赖作用域import的出现就是为了解决以上问题,它可以通过非继承的方式批量引入另一个依赖项中
dependencyManagement元素中定义的依赖项,如下所示:
<dependencyManagement>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-bom</artifactId>
<version>${spring-session-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
说明:<scope>import</scope>只能用在dependencyManagement下type为pom的dependency中。
以上代码等价于添加了以下6个依赖项:

可以看出,使用<scope>import</scope>可以模块化的管理依赖项,提高复用性,pom文件也更加简洁。
3.6 区别
综上所述,各个依赖作用域的区别如下表所示:
| scope取值 | 编译时可用 | 测试时可用 | 运行时可用 | 是否参与打包 | 依赖传递 |
|---|---|---|---|---|---|
| compile | √ | √ | √ | √ | √ |
| provided | √ | √ | × | × | × |
| runtime | × | √ | √ | √ | √ |
| test | × | √ | × | × | × |
4. 影响依赖传递的因素
4.1 依赖作用域(scope)
依赖作用域会影响依赖传递,从上表可以看出,如果scope为provided或者test,该依赖不会传递,只有scope为compile或者runtime,
该依赖才会传递。
4.2 可选依赖(optional)
通过dependency标签引入依赖时,可以通过<optional>指定该依赖是不是可选的,默认值为false:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.2.3</version>
<optional>true</optional>
</dependency>
如果<optional>值为true,那么这个依赖不会传递。
聊聊Maven的依赖传递、依赖管理、依赖作用域的更多相关文章
- Java开发学习(二十九)----Maven依赖传递、可选依赖、排除依赖解析
现在的项目一般是拆分成一个个独立的模块,当在其他项目中想要使用独立出来的这些模块,只需要在其pom.xml使用<dependency>标签来进行jar包的引入即可. <depende ...
- Maven依赖传递、依赖传递排除、依赖冲突
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6628429.html 一:Maven依赖传递 假如有Maven项目A,项目B依赖A,项目C依赖B.那么我们可 ...
- mavean的依赖传递和排除依赖
三个mavean项目 A .B. C 如果B依赖A(A先执行clean package命令) 那么B得pom.xml文件里面就写 <dependency> <groupId> ...
- maven依赖传递和排除依赖冲突
1 依赖的传递 假如 A项目 依赖 a.jar 1.0.1,b.jar 1.0.1,没有直接依赖c.jar 1.0.1,但是b.jar 1.0.1依赖了c.jar 1.0.1,可以说A项目间接依赖了c ...
- maven的中传递依赖,maven的依赖管理(转)
在maven的pom文件中 <dependencies> <dependency> <groupId>junit</groupId> <artif ...
- POM(project Object Model) Maven包管理依赖 pom.xml文件
什么是POM POM全称为“Project Object Model”,意思是工程对象模型.Maven工程使用pom.xml来指定工程配置信息,和其他文本信息.该配置文件以xml为格式,使用xml语法 ...
- 10.Maven依赖排除 禁止依赖传递 取消依赖的方法
转自:https://www.cnblogs.com/duanxz/p/6084494.html 大家都知道Maven的优点是依赖管理,特别是前期使用ANT的开发者都有很多感触.最近要开发一个java ...
- 说说maven依赖冲突,依赖调解,依赖传递和依赖范围
说maven依赖冲突之前需要先说说maven的 依赖传递. 依赖传递 当前项目引入了一个依赖,该依赖的依赖也会被引入项目.更加准确的说法是,maven会解析直接依赖的POM,将那些必要的间接依赖,以传 ...
- Maven依赖范围及依赖传递
一: 依赖范围scope 共5种,compile (编译).test (测试).runtime (运行时).provided.system 不指定,则依赖范围默认为compile. compile:编 ...
- Maven最佳实践:管理依赖
From:http://juvenshun.iteye.com/blog/337405 Maven最佳实践:管理依赖 "If I have seen further it is by sta ...
随机推荐
- Java反射源码学习之旅
1 背景 前段时间组内针对"拷贝实例属性是应该用BeanUtils.copyProperties()还是MapStruct"这个问题进行了一次激烈的battle.支持MapStru ...
- Linux 上的 .NET 如何自主生成 Dump
一:背景 1. 讲故事 前几天微信上有位朋友找到我,说他程序的 线程数 会偶发性瞬时飙高,让我看下大概是什么原因,截图如下: 如果这种问题每天都会出现,比较好的做法就是用 dotnet-trace 捕 ...
- 压制GIF做的一点点小尝试 以及ezgif的基本功能使用
事情的起因 首先群友给我整了个loli莉音的视频 很可爱 但是用qq接收的视频没法一直在那边kawaii 图片本身很小其实 但是转gif就很大 转出来的gif的大小就大的唏嘘 寻找问题 这就是mp4的 ...
- 如何使用CCXT交易数字货币现货
更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. 数字货币现货标准化接口 数字货币市场与股票.期货市场最大的不同点在于数字货币主流交易所数量很多.举个例子 ...
- 离线自动化部署CDH
离线CDH集群自动化部署工具 离线CDH集群安装与部署的自动化脚本工具,简单支持「离线一键装机」. 脚本将对系统配置做出一定修改,使用前请务必确认当前服务器无其他人员.任务使用,以免造成不必要的麻烦, ...
- MyBatis 常用工具类
SQL 类 MyBatis 提供了一个 SQL 工具类,使用这个工具类,我们可以很方便在 Java 代码动态构建 SQL 语句 String newSql = new SQL() ({ SELECT( ...
- tmux 移动窗格
pre + Ctrl+o:所有窗格向前移动一个位置,第一个窗格变成最后一个窗格 pre + Shift+[:当前窗格与上一个窗格交换位置 pre + Shift+]:当前窗格与下一个窗格交换位置
- 线上问题排查--进程重启失败,最后发现是忘了cd
背景 我前面写了几篇文章,讲c3p0数据库连接池发生了连接泄露,但是随机出现,难以确定根因,最终呢,为了快速解决问题,我是先写了个shell脚本,脚本主要是检测服务的接口访问日志,看看过去的30s内是 ...
- 十 Appium环境搭建(Windows版)
注:appium安装到C盘,node.js安装到C盘 一.安装node.js 1.到官网下载node.js:https://nodejs.org/en/download/ 2.获取到安装文件后,直接双 ...
- ES6之清楚明白的使用类(class)
定义 类是构造函数.原型链的语法糖. 定义类有两种方式 class Student { } var Student = class { } 某些浏览器可能无法解析es6及以上的语法,这时候需要通过ba ...
