Maven提高篇系列之五——处理依赖冲突
个人分类: Maven
不知道你在使用Maven时是否遇到过诸如"NoSuchMethodError"或"ClassNotFoundException"之类的问题,甚至发生这些问题的Java类你没都没有听说过。要搞清楚这里面的缘由,我们得学习Maven对依赖冲突的处理机制。
Maven采用“最近获胜策略(nearest wins strategy)”的方式处理依赖冲突,即如果一个项目最终依赖于相同artifact的多个版本,在依赖树中离项目最近的那个版本将被使用。让我们来看看一个实际的例子。
请下载本文的github源代码:https://github.com/davenkin/maven-dependency-conflict-demo
我们有一个web应用resolve-web,该工程依赖于project-A和project-B,project-A依赖于project-common的1.0版本并调用其中的sayHello()方法。project-B依赖于project-C,而project-C又进一步依赖于project-common的2.0版本并调用其中的sayGoodBye()方法。project-common的1.0和2.0版本是不同的,1.0中之包含sayHello()方法,而2.0中包含了sayHello()和sayGoodBye()两个方法。整个项目的依赖关系如下图:
根据Maven的transitive依赖机制,resolve-web将同时依赖于project-common的1.0和2.0版本,这就造成了依赖冲突。而根据最近获胜策略,Maven将选择project-common的1.0版本作为最终的依赖。这和Gradle不同,Gradle在默认情况下将选择最新的版本作为获胜版本。而对于Maven,由于proejct-common的1.0版本比2.0版本在依赖树中离resolve-web更近,故1.0版本获胜。在resolve-web中执行"mvn dependency:tree -Dverbose"可以看到resolve-web的依赖关系:
[INFO] resolve-web:resolve-web:war:1.0-SNAPSHOT
[INFO] +- junit:junit:jar:3.8.1:test
[INFO] +- project-B:project-B:jar:1.0:compile
[INFO] | \- project-C:project-C:jar:1.0:compile
[INFO] | \- (project-common:project-commmon:jar:2.0:compile - omitted for conflict with 1.0)
[INFO] +- project-A:project-A:jar:1.0:compile
[INFO] | \- project-common:project-commmon:jar:1.0:compile
[INFO] \- javax.servlet:servlet-api:jar:2.4:provided
由上可知,project-common:project-commmon:jar:2.0被忽略掉了。此时在resolve-web的war包中将只包含project-common的1.0版本,于是问题来了。由于project-common的1.0版本中不包含sayGoodBye()方法,而该方法正是project-C所需要的,所以运行时将出现“NoSuchMethodError”。(请根据本文github工程中的README.md中的步骤重现该错误信息。)
解决办法
对于这种有依赖冲突所导致的问题,我们有两种解决方法。
方法1:显式加入对project-common 2.0版本的依赖。先前的2.0版本不是离resolve-web远了点吗,那我们就直接将它作为resolve-web的依赖,这不就比1.0版本离resolve-web还近吗?在resove-web的pom.xml文件中直接加上对project-common 2.0 的依赖:
<dependency>
<groupId>project-common</groupId>
<artifactId>project-commmon</artifactId>
<version>2.0</version>
</dependency>
方法2:resolve-web对project-A的dependency声明中,将project-common排除掉。在resolve-web的pom.xml文件中修改对project-A的dependency声明:
<dependency>
<groupId>project-A</groupId>
<artifactId>project-A</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>project-common</groupId>
<artifactId>project-commmon</artifactId>
</exclusion>
</exclusions>
</dependency>
此时再在resolve-web中执行"mvn dependency:tree -Dverbose",结果如下:
......
[INFO] resolve-web:resolve-web:war:1.0-SNAPSHOT
[INFO] +- junit:junit:jar:3.8.1:test
[INFO] +- project-B:project-B:jar:1.0:compile
[INFO] | \- project-C:project-C:jar:1.0:compile
[INFO] | \- project-common:project-commmon:jar:2.0:compile
[INFO] +- project-A:project-A:jar:1.0:compile
[INFO] \- javax.servlet:servlet-api:jar:2.4:provided
......
此时的依赖树中已经不包含project-common的1.0版本了。
另外,我们还可以在project-A中将对project-common的依赖声明为optional,optional即表示非transitive,此时当在resolve-web中引用project-A时,Maven并不会将project-common作为transitive依赖自动加入,除非有别的项目(比如project-B)声明了对project-common的transitive依赖或者我们在resolve-web中显式声明对project-common的依赖(方法一)。
Maven提高篇系列之五——处理依赖冲突的更多相关文章
- Maven提高篇系列之(五)——处理依赖冲突
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...
- Maven提高篇系列之(六)——编写自己的Plugin(本系列完)
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...
- Maven提高篇系列之(四)——使用Profile
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...
- Maven提高篇系列之(三)——使用自己的Repository(Nexus)
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...
- Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例)
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...
- Maven提高篇系列之(一)——多模块 vs 继承
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...
- Maven 基础(二) | 解决依赖冲突的正确姿势
一.依赖原则 假设,在 JavaMavenService2 模块中,log4j 的版本是 1.2.7,在 JavaMavenService1 模块中,它虽然继承于 JavaMavenService2 ...
- chessy 提高篇系列 阅读笔记
java提高篇(一)—–理解java的三大特性之封装 封装的好处, 汇聚属性和方法 减少修改对 其他处的影响 控制get和set方法. java提高篇(二)—–理解java的三大特性之继承 继承的好处 ...
- maven解决omitted for duplicate(依赖冲突)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...
随机推荐
- 17.Mysql分区
17.Mysql分区分区是指根据一定的规则把一个表分解成多个部分,逻辑上仍是一张表,实际上由多个物理分区对象组成.分区对于应用是完全透明的,不影响业务逻辑和SQL编写.分区的优点: 可以存储更多的数据 ...
- 【转】我为什么把think in java 读了10遍
我在想写这篇博文之前,就曾经对我媳妇(她是做web前端的)讲,我把think in java看了几次几次,媳妇那时就用很羡慕和莫名的眼神看着我说,你真有毅力,我当时就蒙了,我以为她会说,你现在基础一定 ...
- DataTable表连接
public static System.Data.DataTable TableJoin(System.Data.DataTable dt, System.Data.DataTable dtDeta ...
- Elastix 安装G729 G723语音编码
下載適合自己機器及軟體版本的模組檔,基本上略分為 pentium/pentium2/pentium3/x86_64,Asterisk 1.2/1.4/1.6.前往 http://asterisk.ho ...
- Spring Boot学习笔记:传统maven项目与采用spring boot项目区别
项目结构区别 传统的maven构建的项目结构如下: 用maven构建的采用springboot项目结构如下: 二者结构一致,区别如下:传统项目如果需要打成war包,需要在WEB-INF目录结构配置we ...
- Python中逗号的妙用
闲着没事打算用Python刷一遍pat,输出过程中遇到了一个这样的问题: 题目1002题目要求 在一行内输出n的各位数字之和的每一位,拼音数字间有1 空格,但一行中最后一个拼音数字后没有空格, 但是P ...
- 2018.11.06 bzoj1835: [ZJOI2010]base 基站选址(线段树优化dp)
传送门 二分出每个点不需要付www贡献的范围,然后可以推出转移式子: f[i][j]=f[i−1][k]+value(k+1,j)+c[i]f[i][j]=f[i-1][k]+value(k+1,j) ...
- 使用Ant发布web应用到tomcat
使用Ant发布web应用到tomcat 来自:http://blog.csdn.net/hbcui1984/article/details/1954537 今天在公司用ant写了个部署web应用的脚本 ...
- GDI基础(3):绘制图片
1.CBitmap位图类封装了Windows GDI中的位图和操作位图的成员函数.CPen.CBrush.CFont.CBitmap是常用的Windows GDI对象,和CFont一样,CBitmap ...
- 哪些优秀的 Windows 小工具,类似 clover 或 everything
有哪些优秀的 Windows 小工具,类似 clover 或 everything? 目前已知的有everything, listary, total commander, clover, dexpo ...