这是一个Maven提高篇的系列,包含有以下文章:

  1. Maven提高篇系列之(一)——多模块 vs 继承
  2. Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例)
  3. Maven提高篇系列之(三)——使用自己的Repository(Nexus)
  4. Maven提高篇系列之(四)——使用Profile
  5. Maven提高篇系列之(五)——处理依赖冲突
  6. Maven提高篇系列之(六)——编写自己的Plugin(本系列完)

在本系列的上一篇文章中,我们讲到了如何使用Profile,在本文中,我们将讲到如何处理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.: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声明:

此时再在resolve-web中执行"mvn dependency:tree -Dverbose",结果如下:

......

[INFO] resolve-web:resolve-web:war:1.0-SNAPSHOT

[INFO] +- junit:junit:jar:3.8.: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的依赖(方法一)。

下一篇文章中,我们将讲到如何编写自己的Plugin。

Maven提高篇系列之(五)——处理依赖冲突的更多相关文章

  1. Maven提高篇系列之(六)——编写自己的Plugin(本系列完)

    这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...

  2. Maven提高篇系列之(四)——使用Profile

    这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...

  3. Maven提高篇系列之(三)——使用自己的Repository(Nexus)

    这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...

  4. Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例)

    这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...

  5. Maven提高篇系列之(一)——多模块 vs 继承

    这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...

  6. Maven提高篇系列之五——处理依赖冲突

    个人分类: Maven 不知道你在使用Maven时是否遇到过诸如"NoSuchMethodError"或"ClassNotFoundException"之类的问 ...

  7. chessy 提高篇系列 阅读笔记

    java提高篇(一)—–理解java的三大特性之封装 封装的好处, 汇聚属性和方法 减少修改对 其他处的影响 控制get和set方法. java提高篇(二)—–理解java的三大特性之继承 继承的好处 ...

  8. oracle(sql)基础篇系列(五)——PLSQL、游标、存储过程、触发器

      PL/SQL PL/SQL 简介 每一种数据库都有这样的一种语言,PL/SQL 是在Oracle里面的一种编程语言,在Oracle内部使用的编程语言.我们知道SQL语言是没有分支和循环的,而PL语 ...

  9. java提高篇(二五)-----HashTable

          在java中与有两个类都提供了一个多种用途的hashTable机制,他们都可以将可以key和value结合起来构成键值对通过put(key,value)方法保存起来,然后通过get(key ...

随机推荐

  1. asp.net identity 2.2.0 中角色启用和基本使用(四)

    创建角色相关视图 第一步:添加视图   打开RolesAdminController.cs   将鼠标移动到public ActionResult Index()上  右键>添加视图   系统会 ...

  2. PostgreSQL基础整理(二)

    存储过程 实现功能:针对工资表30岁以下,工资提升10% 30至40提升20% 40以上提升30% + 奖金(入参)返回平均薪酬 创建表: DROP TABLE emps; CREATE TABLE ...

  3. Java多线程2:Thread中的实例方法

    Thread类中的方法调用方式: 学习Thread类中的方法是学习多线程的第一步.在学习多线程之前特别提出一点,调用Thread中的方法的时候,在线程类中,有两种方式,一定要理解这两种方式的区别: 1 ...

  4. kali linux Python开发环境初始化

    kali linux Python 黑客编程1 开发环境初始化 为什么要选择Python? Python作为目前Linux系统下最流行的编程语言之一,对于安全工作者的作用可以和C++相提并论.Pyth ...

  5. ngModel 值不更新/显示

    angular中的$scope是页面(view)和数据(model)之间的桥梁,它链接了页面元素和model,也是angular双向绑定机制的核心. 而ngModel是angular用来处理表单(fo ...

  6. Oracle存在修改,不存在插入记录

    接触编程以来,在数据存储方面一直用的MS SQL.Oracle这名字对我来说是如此的熟悉,但是对其内容却很陌生,最近公司的一个项目用起了Oracle,所以也开始高调的用起了Oracle.在没有接触Or ...

  7. node.js调试

    用了几天node.js感觉很新奇,但是调试问题实在是愁煞人,开始的时候懒的学习调试方法,看看异常内容就可以了,但随着代码复杂程度的上升,并不是所有错误都是语法错误了,不调试搞不定了,只好搜搜资料,学习 ...

  8. 微软 Build 2016年开发者大会发布多项功能升级

    微软Build 2016开发者大会在美国旧金山的莫斯康展览中心开幕.本次大会对一些重点功能进行了完善.如手写笔支持技术Windows Ink.语音识别Cortana应用集(Cortana Collec ...

  9. Macaca-iOS入门那些事

    Macaca-iOS入门那些事 一. 前言 最近有朋友向我请教iOS自动化测试框架选型,刚好前段时间做讲座时发现了macaca,遂试用.所以,下面的内容会只围绕Macaca的iOS部分. 二. Mac ...

  10. linux shell program summary

    from:Sep 23 2016 mathematical operation: floating number,bc calculator: we can also use bc in shell ...