前言

当项目中引用多个jar时,经常会发生冲突。主要发生在同一个jar,有不同的版本,导致加载时,选择错误了。这里的选择指maven机制选择,或者文件读取加载选择造成的。

出现问题

出现jar冲突时,经常会发生一些错误。因为同样jar的不同版本,含有的内容都不一样的。有时候是编译期出现,有时候时运行期出现的。可能出现以下情况:

  • java.lang.ClassNotFoundException 选择版本不正确,找不到class
  • java.lang.NoSuchMethodError 同上,选择版本不正常,或者类加载不正确
  • 没有报错异常,但应用的行为跟预期不一致 加载错误版本,逻辑不一致

**

具体问题分类

  • 项目同一依赖应用,存在多版本,每个版本同一个类,可能存在差异。
  • 项目不同依赖应用,存在包名,类名完全一样的类。

**

maven仲裁机制

如果是多个jar版本存在,当maven引入多个jar时,如果jar相同就会发生隐式引用。因为整个JVM中,最后只会加载唯一的版本。以下是maven面对多jar时的仲裁机制:

  • 优先按照依赖管理元素中指定的版本声明进行仲裁,此时下面的两个原则都无效了
  • 若无版本声明,则按照“短路径优先”的原则(Maven2.0)进行仲裁,即选择依赖树中路径最短的版本
  • 若路径长度一致,则按照“第一声明优先”的原则进行仲裁,即选择POM中最先声明的版本

Jar加载顺序

jar的加载可以跟进maven的仲裁方式来解决,当不同jar含有相同class时,这时候maven就无法处理了,由加载顺序决定。

  • Jar包所处的加载路径,或者换个说法就是加载该Jar包的类加载器在JVM类加载器树结构中所处层级。Java是双亲委派方式加载的,不同的目录,加载的顺序不一致,比如:/JAVAHOME/lib是最最基础加载的,/JAVAHOME/lib/ext是此次类加载的,等等还有后续的类加载器加载。
  • 文件的加载顺序加载不同。tomcat等等加载jar时,顺序路径不同造成了加载的class不一样。

解决办法

发生冲突时,可以使用idea自带的maven插件分析冲突,或者采取maven自带的命令来分析依赖:

  1. mvn dependency:tree -Dverbose -Dincludes=<groupId>:<artifactId>

找到冲突之后,如何解决:

  1. 通用性办法是采取去除冲突的jar,常用。
  2. 中添加具体的版本,这样maven的仲裁机制生效,就不会自己选择了。
  3. 如果class冲突,排除或者替换jar。

避免

遇到问题采取解决也可以最好直接避免。

  1. dependencyManagement管理

采取依赖管理元素,对依赖Jar包进行统一版本管理,一劳永逸。通常的做法是,在parent模块的pom文件中尽可能地声明所有相关依赖Jar包的版本,并在子pom中简单引用该构件即可。

  1. 添加冲突检查插件

使用maven插件maven-enforcer-plugin去解决。

参考