前言
当项目中引用多个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自带的命令来分析依赖:
mvn dependency:tree -Dverbose -Dincludes=<groupId>:<artifactId>
找到冲突之后,如何解决:
- 通用性办法是采取
去除冲突的jar,常用。 - 在
中添加具体的版本,这样maven的仲裁机制生效,就不会自己选择了。 - 如果class冲突,排除或者替换jar。
避免
遇到问题采取
- dependencyManagement管理
采取依赖管理元素
- 添加冲突检查插件
使用maven插件maven-enforcer-plugin去解决。