好机会,我要帮女同事解决Maven冲突问题

任何一个故事起因最重要

任何一个职业,女生都有绝对的优势。更别提IT行业了,在部门中要是有女程序猿那肯定是香饽饽,备受呵护呀。

之前有一次,一位刚来的妹子遇到问题了,画风顿时就变成上面的图片了,群起而围之,但是最后的结果并不理想,还是得我出马(此处有点小吹牛)。

妹子遇到的是Jar包冲突的问题,错误信息是 Caused by: java.lang.ClassNotFoundException,看错误要么就是缺少某个Jar包,要么就是冲突了。

其实在工作中经常会遇到这种冲突的问题,比如:Caused by:java.lang.NoSuchMethodError 这个异常信息也是冲突导致的,想要解决冲突问题就必须得知道哪里冲突了(好像是废话)。

大部分都是用Maven来管理依赖的Jar,今天这篇文章主要是讲解如何解决Maven带来的依赖冲突问题。

Maven回顾

Maven自述

Maven 是用于构建和管理Java项目的工具。对于Java方向的来说,Maven几乎都要接触和使用。当然也有其他的工具来代替Maven,比如Ant和Gradle。

之前有接触过Grails构建的Java Web项目,就是用Gradle来做依赖管理的。至于Ant也在刚工作的时候在一些老项目中有见到过,后面几乎没见过了。

Maven文档地址:https://maven.apache.org

使用Maven可以让我们快速构建一个新的项目,并且很方便的可以集成和管理多个三方的框架。当我们需要某个框架时可以去搜索一下这个框架的信息,然后配置到你的项目中即可。

搜索地址:https://mvnrepository.com

比如我们想要使用Spring Boot,除了在Spring的文档中获取依赖的版本,也可以自己去搜索,选择对应的版本,如下图:

可以看到默认就是Maven的依赖方式,只需要将dependency整段内容复制到项目的pom.xml文件中即可。右侧还有很多其他的依赖方式,比如Gradle等。

Maven依赖传递

今天主要讲下如何去解决Maven做依赖管理的时候Jar包冲突的问题,在解决之前先来了解下基本的知识。

上图展示了Maven的依赖传递性,首先是项目B中依赖了Spring和Guava两个框架。然后项目A又依赖了项目B,所以项目A也会依赖Spring和Guava两个框架。

依赖传递Jar包选择逻辑

依赖性传递会导致项目中依赖很多其他版本的Jar,这种情况下怎么进行Jar包的选择呢?

有两个规则:

  • 不同距离,距离近优先
  • 相同距离,前者优先

如下图所示,项目依赖了项目A和项目B,A和B分别依赖了Guava,但是从依赖层次来看,项目B的层次更浅,故Guava18.0会被优先选择。

当距离相同的时候,就会优先选择定义在前面的,如下图所示,项目A和项目B都分别依赖了Guava15.0和Guava18.0的版本,但是项目A的顺序在项目B的前面,所以会优先选择Guava15.0版本。

通过依赖传递性经常会导致Jar包冲突的问题,比如下图的项目A本身依赖了Guava15.0,然后又依赖了项目B,项目B中依赖了Guava18.0,这样项目A就会同时依赖Guava15.0和Guava18.0。

如果刚好用到了高版本不兼容低版本的方法和类时,就会出现选择错误,因为Maven会根据依赖树的深浅来选型浅的依赖,也就是15.0。

冲突案例

下面就是一个典型的Jar包冲突问题,当一个Jar有多个版本的时候,就会出现冲突。

错误信息可以看到com.google.common.collect.FluentIterable.concat这个方法找不到,目前是从guava-18.0.jar中加载的,这种问题我们改怎么解决呢?

Description:
An attempt was made to call the method com.google.common.collect.FluentIterable.concat(Ljava/lang/Iterable;Ljava/lang/Iterable;)Lcom/google/common/collect/FluentIterable; but it does not exist. Its class, com.google.common.collect.FluentIterable, is available from the following locations:
? ? jar:file:/Users/yinjihuan/.m2/repository/com/google/guava/guava/18.0/guava-18.0.jar!/com/google/common/collect/FluentIterable.class
It was loaded from the following location:
? ? file:/Users/yinjihuan/.m2/repository/com/google/guava/guava/18.0/guava-18.0.jar

Action:
Correct the classpath of your application so that it contains a single, compatible version of com.google.common.collect.FluentIterable

解决思路之悬丝诊脉

找出冲突的Jar,看看当前项目中依赖了哪几个版本

Eclipse

在Eclipse中可以双击pom文件,进入Dependency视图,输入你要搜索的jar名称进行搜索,就可以看出当前项目中哪些框架依赖了你搜索的jar,什么版本都能知道。

Idea

Idea中可以安装maven helper插件来查看相关依赖信息,默认选中Conflicts会展示当前项目存在冲突的依赖,当然我们也可以直接查看树形的依赖关系去分析冲突。

Maven命令

不用不借助于开发工具的插件,我们可以直接用Maven命令来查看当前项目的依赖关系,命令行进入到你要分析的项目目录下,执行下面的命令将分析结果保存到文件中:

mvn dependency:tree > tree.log

执行完之后依赖的信息结构如下:

搜索了下guava,发现在smjdbctemplate中依赖了18.0版本,这个框架是我自己基于jdbctemplate封装的一个框架。

解决思路之察言观色

其实很明显,错误信息已经告诉我们18.0中找不到concat方法,所以18.0肯定是不能用的,通过前面的分析,找到了直接依赖guava.18.0.jar的是smjdbctemplate,解决办法就是将smjdbctemplate中的guava排除掉。

<dependency>
    <groupId>com.github.yinjihuan</groupId>
    <artifactId>smjdbctemplate</artifactId>
    <version>1.1</version>
    <exclusions>
        <exclusion>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </exclusion>
    </exclusions>
</dependency>

还有就是根据依赖树的深浅度来判断当前项目依赖的是哪个版本,如下图:

18.0是最浅的,肯定是依赖它,其实在Eclipse里面直接查看Maven Dependencies就可以指定当前项目依赖哪些框架和版本信息,如下图:

当我们排除掉18.0后再来看依赖的版本是20.0,如下图:

根据依赖树的深浅度,20.0和19.0都是一样的层级,但是20.0在19.0前面,所以优先选择20.0版本。

再来看项目中的pom文件,发现swagger的声明顺序在apollo的前面。

如果我们把顺序调整一下,那么就会依赖19.0的版本。

总结

通过我仔细耐心的讲解,妹子终于自己解决了遇到的问题,后面的事你们就猜去吧。??

这种问题其实无法避免,当你依赖的三方框架越多的时候,冲突的可能性就越大。碰到问题的时候沉下心来仔细分析,借助于工具帮助你排查问题。

当然我们在自己项目中去依赖三方的框架,也是要注意版本的问题,特别是对于多模块的项目,每个子模块都去依赖不同的版本,这样很容易出问题,一般建议在父pom中dependencyManagement来统一管理版本,子模块直接统一使用父pom中定义好的版本。

还有就是可以使用optional来设置可选依赖,比如说你要封装一个通用的模块Common,这个模块中有很多通用的功能,项目A依赖只需要使用功能A,项目B依赖只需要使用功能B。每个功能都依赖了三方的Jar,这个时候如果你不做任何处理,只要依赖了你这个通用的模块Common,那么也就会间接依赖这两个功能的第三方Jar。这个时候可以通过设置optional=true来解决这个问题,我依赖了你的通用模块Common,如果我要使用A功能,那么我必须显示依赖A功能需要的三方依赖才可以。

原文地址:https://www.cnblogs.com/yinjihuan/p/12298916.html

时间: 2024-10-07 20:22:49

好机会,我要帮女同事解决Maven冲突问题的相关文章

jQuery基础之(五)jQuery自定义添加&quot;$&quot;与解决&quot;$&quot;的冲突

1.自定义添加$ 从上面四篇文章我们看到jQuery的强大,但无论如何,jQuery都不可能满足所有用户的需求,而且有一些需求十分小众,也不适合放到整个jQuery框架中,正是因为这一点,jQuery提供了用户自定义添加“$”的方法. 代码如下: $.fn.disable = function() { return this.each(function() { if (typeof this.disabled != "undefined") this.disable = true; }

JavaScript解决命名冲突的一种方法

过程化编码 过程化编码, 表现为 定义若干函数,然后调用定义函数, 随着页面交互逻辑变化, 从简单到复杂, 定义的所有函数.和变量 都挂在 window对象上, window对象 编程者子自定义变量名称 规模会愈来愈额庞大,在后面开发和维护的过程中, 很容易导致函数名称冲突,引起意想不到问题. 例如, 之前有个同事定义了 一个 sample 函数, N长时间后, 另一个同事又定义了一个含义不同的同名函数sample,则前以同事的代码就有问题了. 模块化方法一则 JS函数内部相当于一个小的程序空间

解决 Git 冲突的 14 个建议和工具

Git 非常善于合并代码.代码的合并在本地完成,快速而且灵活.正常情况下每次从不同分支合并内容时,冲突有可能会发生.通常解决冲突很简单,就如同知道(如何)选择(保留)重要的更改一样,而有时解决冲突则需要额外的工作. 每个开发者对于解决冲突有不同的偏好.不久前,一位叫丹·史蒂文斯的同事用内部软件 Questions for Confluence 询问了大家是如何做的. 收集到的回答和看法比 Atlassian 之墙有更大的吸引力.下面是我们用多种方式解决 Git 冲突的详尽描述,希望它能提供一些可

IDEA 解决 Maven 依赖冲突的高能神器,这一篇够不够?

? 1.何为依赖冲突 Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的.Maven的依赖机制会导致Jar包的冲突.举个例子,现在你的项目中,使用了两个Jar包,分别是A和B.现在A需要依赖另一个Jar包C,B也需要依赖C.但是A依赖的C的版本是1.0,B依赖的C的版本是2.0.这时候,Maven会将这1.0的C和2.0的C都下载到你的项目中,这样你的项目中就存在了不同版本的C,这时Maven会依据依赖路径最短优先原则,来决定使用哪个版本的Jar包,而另一个无用的Jar包则未被使用,

大厂面试必问!HashMap 怎样解决hash冲突?

HashMap冲突解决方法比较考验一个开发者解决问题的能力. 下文给出HashMap冲突的解决方法以及原理分析,无论是在面试问答或者实际使用中,应该都会有所帮助. 在Java编程语言中,最基本的结构就是两种,一种是数组,一种是模拟指针(引用),所有的数据结构都可以用这两个基本结构构造,HashMap也一样. 当程序试图将多个 key-value 放入 HashMap 中时,以如下代码片段为例: HashMap<String,Object>m=newHashMap<String,Objec

C++拾遗--多线程:原子操作解决线程冲突

C++拾遗--多线程:原子操作解决线程冲突 前言 在多线程中操作全局变量一般都会引起线程冲突,为了解决线程冲突,引入原子操作. 正文 1.线程冲突 #include <stdio.h> #include <stdlib.h> #include <process.h> #include <Windows.h> int g_count = 0; void count(void *p) { Sleep(100); //do some work //每个线程把g_c

解决Maven默认仓库没有的jar下载(二)

前言: 在 “解决Maven不能下载“oracle.aspectjweaver.com.springsource.net.sf.cglib”jar(http://www.cnblogs.com/wql025/p/4996484.html )”一文中我写到了如果像oracle这样的jar不能在maven默认仓库下载的解决方法.现在我知道了一个非常强大的Maven仓库地址: http://maven.oschina.net/home.html 可能很多小伙伴已知道啦. 所以当有些jar不能下载可以在

解决maven生成的web项目下的servlet.jar与tomcat自带servlet.jar冲突的问题

使用maven生成web工程后,编译需要下servlet-api.jar和jsp-api.jar文件. pom文件中的写法为: <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> 这种写法在使用mvn targe命令时有效的,

Operation System - Peterson&amp;#39;s Solution算法 解决多线程冲突

Person's solution 是用来一种基于软件的解决关键区域问题的算法(critical-section). 它并不是完美的,有可能不对地工作.并且是限制解决两个进程同步的问题. 可是它非常easy,非常原始,学习起来也是非常轻松的. 代码例如以下: do { flag[i] = true; turn = j; while (flag[j] && turn == j); critical section flag[i] = false; remainder section } wh