Maven依赖冲突解决总结

maven,依赖,冲突,解决,总结 · 浏览次数 : 1214

小编点评

**Jar包冲突的常见表现** * Jar包冲突往往难以排查,因为其异常描述可能包含一些无关的信息。 * Jar包冲突主要有以下几种类型: * **java.lang.ClassNotFoundException**:表示该类在运行时无法找到。 * **java.lang.NoSuchMethodError**:表示方法不存在。 * **java.lang.NoClassDefFoundError**:表示类的定义文件无法找到。 * **java.lang.LinkageError**:表示类在运行时无法加载。 **Jar包冲突的本质** Jar包冲突的本质是 Java应用程序因某种因素,加载不到正确的类而导致其行为跟预期不一致。 **解决 Jar包冲突的方法** * 使用 Maven 帮助: * 使用 `MavenHelper` 中的 `Conflicts` 方法。 * 使用 `-cp` 参数指定 Jar 包加载顺序。 * 使用 `-Djava.class.path` 设置 classpath。 * 使用 `-Xbootclasspath/` 和 `-Xbootclasspath/` 等开关设置 bootclasspath。 * 设置 classpath: * 使用 `path` 属性指定 jar 包路径。 * 使用 `separator` 分隔多个 jar 包路径。 * 使用依赖调节: * 使用 `dependencyManagement` 中的 `include` 和 `exclude` 元素定义依赖和排除 jar 包。 * 使用 `dependencyResolution` 中的 `override` 和 `fallback` 元素进行依赖排序。

正文

转载请注明出处:

1.Jar包冲突的通常表现

Jar包冲突往往是很诡异的事情,也很难排查,但也会有一些共性的表现。

  • 抛出java.lang.ClassNotFoundException:典型异常,主要是依赖中没有该类。导致原因有两方面:第一,的确没有引入该类;第二,由于Jar包冲突,Maven仲裁机制选择了错误的版本,导致加载的Jar包中没有该类。

  • 抛出java.lang.NoSuchMethodError:找不到特定的方法。Jar包冲突,导致选择了错误的依赖版本,该依赖版本中的类对不存在该方法,或该方法已经被升级。

  • 抛出java.lang.NoClassDefFoundError,java.lang.LinkageError等,原因同上。

  • 没有异常但预期结果不同:加载了错误的版本,不同的版本底层实现不同,导致预期结果不一致。

2.Jar包冲突的本质

Jar包冲突的本质:Java应用程序因某种因素,加载不到正确的类而导致其行为跟预期不一致

具体分两种情况:

  • 情况一:项目依赖了同一Jar包的多个版本,并且选错了版本;

  • 情况二:同样的类在不同的Jar包中出现,导致JVM加载了错误的类;

  情况一,同一个依赖引入了多个Jar包版本,不同的Jar包版本有不同的类和方法。由于Maven依赖树的仲裁机制导致Maven加载了错误的Jar包,从而导致Jar包冲突;

  情况二,同一类在不同的Jar包中出现。这种情况是由于JVM的同一个类加载器对于同一个类只会加载一次,现在加载一个类之后,同全限定名的类便不会进行加载,从而出现Jar包冲突的问题。

  针对第二种情况,如果不是类冲突抛出了异常,你可能根本意识不到,所以就显得更为棘手。这种情况就可以采用前文所述的通过分析不同类加载器的优先级及加载路径、文件系统的文件加载顺序等进行调整来解决。

3.解决Jar包冲突的方法

  几种场景下解决Jar冲突的方法:

    • Maven默认处理:路径最近者优先和第一声明优先;

    • 排除法:使用 Maven Helper,可以将冲突的Jar包在pom.xml中通过exclude来进行排除;

    • 版本锁定法:如果项目中依赖同一Jar包的很多版本,一个个排除非常麻烦,此时可用版本锁定法,即直接明确引入指定版本的依赖。此种方式的优先级最高。

    • 通过设置classpath指定jar包加载的先后顺序 

3.1Mavenhelper 解决冲突

  

  具体步骤:  

    1.使用mavenHelper 选择 Confilicts;

    2.如果列表存在冲突的依赖,则点击查看冲突的详情

    3.对冲突的依赖可进行右键;使用 exclude 进行排除即可解决冲突

3.2 通过设置 classpath 指定jar包加载的先后顺序

  第一种方式:
java -jar -classpath C:\dependency\framework.jar:C:\location\otherFramework.jar  test.jar

  第二种方式(-cp 等于 -classpath):

java -jar -cp C:\dependency\framework.jar:C:\location\otherFramework.jar  test.jar

  第三种方式:

-Djava.class.path=a.jar

  第四种方式:

-Xbootclasspath/a: 

  classpath 理解和设置可以查看这篇文章Tomcat 与 JVM 中classpath的理解和设置总结 

4.Maven管理机制

依赖传递原则

  当在Maven项目中引入A的依赖,A的依赖通常又会引入B的jar包,B可能还会引入C的jar包。这样,当你在pom.xml文件中添加了A的依赖,Maven会自动的帮你把所有相关的依赖都添加进来。

  如下图所示,项目 A 依赖于项目 B,B 又依赖于项目 C,此时 B 是 A 的直接依赖,C 是 A 的间接依赖。

 

  Maven 的依赖传递机制是指:不管 Maven 项目存在多少间接依赖,POM 中都只需要定义其直接依赖,不必定义任何间接依赖,Maven 会动读取当前项目各个直接依赖的 POM,将那些必要的间接依赖以传递性依赖的形式引入到当前项目中。Maven 的依赖传递机制能够帮助用户一定程度上简化 POM 的配置。

  基于 A、B、C 三者的依赖关系,根据 Maven 的依赖传递机制,我们只需要在项目 A 的 POM 中定义其直接依赖 B,在项目 B 的 POM 中定义其直接依赖 C,Maven 会解析 A 的直接依赖 B的 POM ,将间接依赖 C 以传递性依赖的形式引入到项目 A 中。

   即当一个依赖需要另外一个依赖支撑时,Maven会帮我们把相应的依赖依次添加到项目当中。这样的好处是,使用起来就非常方便,不用自己挨个去找依赖Jar包了。坏处是会引起Jar包冲突,

最短路径优先原则

  但当一个间接依赖存在多条引入路径时,为了避免出现依赖重复的问题,Maven 通过依赖调节来确定间接依赖的引入路径。

  依赖调节遵循以下两条原则:

    1. 引入路径短者优先

    2. 先声明者优先

  以上两条原则,优先使用第一条原则解决,第一条原则无法解决,再使用第二条原则解决。

引入路径短者优先

  引入路径短者优先,顾名思义,当一个间接依赖存在多条引入路径时,引入路径短的会被解析使用。

  例如,A 存在这样的依赖关系: A->B->C->D(1.0) A->X->D(2.0)

  D 是 A 的间接依赖,但两条引入路径上有两个不同的版本,很显然不能同时引入,否则造成重复依赖的问题。根据 Maven 依赖调节的第一个原则:引入路径短者优先,D(1.0)的路径长度为 3,D(2.0)的路径长度为 2,因此间接依赖 D(2.0)将从 A->X->D(2.0) 路径引入到 A 中。

先声明者优先

  先声明者优先,顾名思义,在引入路径长度相同的前提下,POM 文件中依赖声明的顺序决定了间接依赖会不会被解析使用,顺序靠前的优先使用。

  例如,A 存在以下依赖关系: A->B->D(1.0) A->X->D(2.0)

  D 是 A 的间接依赖,其两条引入路径的长度都是 2,此时 Maven 依赖调节的第一原则已经无法解决,需要使用第二原则:先声明者优先。

5.Tomcat启动时Jar包和类的加载顺序

最后,梳理一下Tomcat启动时,对Jar包和类的加载顺序,其中包含上面提到的不同种类的类加载器默认加载的目录:

  • $java_home/lib 目录下的java核心api;
  • $java_home/lib/ext 目录下的java扩展jar包;
  • java -classpath/-Djava.class.path所指的目录下的类与jar包;
  • $CATALINA_HOME/common目录下按照文件夹的顺序从上往下依次加载;
  • $CATALINA_HOME/server目录下按照文件夹的顺序从上往下依次加载;
  • $CATALINA_BASE/shared目录下按照文件夹的顺序从上往下依次加载;
  • 项目路径/WEB-INF/classes下的class文件;
  • 项目路径/WEB-INF/lib下的jar文件;

上述目录中,同一文件夹下的Jar包,按照顺序从上到下一次加载。如果一个class文件已经被加载到JVM中,后面相同的class文件就不会被加载了

 
 
 

与Maven依赖冲突解决总结相似的内容:

Maven依赖冲突解决总结

转载请注明出处: 1.Jar包冲突的通常表现 Jar包冲突往往是很诡异的事情,也很难排查,但也会有一些共性的表现。 抛出java.lang.ClassNotFoundException:典型异常,主要是依赖中没有该类。导致原因有两方面:第一,的确没有引入该类;第二,由于Jar包冲突,Maven仲裁机

Maven进阶学习指南

当我们在开发项目时,有时需要用到外部依赖组件,例如当我们需要Json序列化的时候需要用到FastJson组件,我们可以通过下载对应jar包加载到项目中。但当一个大的项目同时需要依赖各种各样的外部服务,就存在着配置繁琐、依赖冲突等问题,因此可以通过maven来完成对应的依赖管理功能。

Maven依赖管理

本文主要记录Maven依赖管理中关于依赖传递和依赖范围的知识 Maven项目示例 创建3个maven项目,分配依赖log4j 1.2.12, 1.2.13, 1.2.14版本。 com.leo project1

SpringBoot集成Mongodb文档数据库

添加Maven依赖 org.springframework.boot spring-boot-starter-data-mongodb 配置Mongodb连接

聊聊Maven的依赖传递、依赖管理、依赖作用域

1. 依赖传递 在Maven中,依赖是会传递的,假如在业务项目中引入了spring-boot-starter-web依赖: org.springframework.boot spring-boot-starter

springboot接入influxdb

转载请注明出处: 1.添加maven依赖 org.springframework.boot spring-boot-starter

SpringBoot配置与打包基础

本篇主要记录SpringBoot使用的基础配置 SpringBoot Maven配置 SpringBoot maven依赖关系 我们创建springboot项目后,会发现项目的pom文件都会继承自spring-boot-starter-parent这个配置,打开这个父配置文件,会发现它又继承自spr

java netty 实现 websocket 服务端和客户端双向通信 实现心跳和断线重连 完整示例

java netty 实现 websocket 服务端和客户端双向通信 实现心跳和断线重连 完整示例 maven依赖 io.netty netty-all 4.1.97

maven 工程pom依赖优化及常用命令

本文为博主原创,转载请注明出处: 1. mvn dependency:list 列出项目的所有jar包 mvn dependency:list -Dverbose 该命令可以列出项目依赖的所有jar包,-Dverbose参数会把被忽略的jar,即相同jar包的不同版本引入也列出来。 输出示例: 2.

本地已经有项目需要的所有依赖,但是maven总是会去网上下载

情况说明本地已经有项目需要的所有依赖,但是maven总是会去网上下载,因为网络不好等原因,一直下载失败,但是本地明明就已经有依赖了。maven的settings配置 maven已经配置成自己下载的,至于仓库,没有改变路径,但是仓库的settings也改了。maven的settings已经配置成本地仓