最近由于项目的脚手架升级,导致项目中的各个jar包中间产生了很大的冲突,从而启动不起来。
参考了网上的一些方法,分享下走过的坑,若有什么不对的地方,或者疑问的地方,欢迎指出或者一起探讨下啦~
NoSuchMethodError和classNotFoundException
两个问题的解决方法是一样的呢,一个是方法找不到,一个是类不到,我先大概我理解的怎么产生的原因,和解决的版本,如果有什么讲的不对的地方,欢迎指出呀~
先看看问题
运行时抛出 的原因是什么?
当应用程序试图调用类(静态或实例)的指定方法,而该类已不再具有该方法的定义时,就会抛出
java.lang.NoSuchMethodError 错误。简单地说,就是同一个 Class
有多个版本的实现,并且在运行时调用了缺少方法的那个版本。(缺失jar包或是jar冲突)
简单通俗的来讲,就是在项目启动的时候,有两个版本 1.0.0 和1.1.0 ,他们的类名一样 但里面的方法不一致 那么 JVM 类加载器对于同一个类名只会加载一次。这样可能我们需要1.0.0的版本 而他给我们加载了1.1.0的版本 那样就会报错 所以可能就会出现图上那种情况
这样会有个疑问 ,jvm是加载1.0.0或是1.1.0由什么决定的呢?
maven有个依赖仲裁机制
1 版本声明——》 2 是加载路径最短的 ——》3 加载优先调用的
jvm 的也有个加载顺序 — 双亲委派机制
首先,Maven 依赖仲裁机制 决定了打包的优先级, 仲裁优先级“从高到低”如下所述:
-
优先按照依赖管理 [dependencyManagement] 元素中指定的版本进行仲裁;
-
若无版本声明,则按照 “短路径优先” 原则(Maven2.0)进行仲裁,即选择依赖树中路径最短的版本;
-
若路径长度一致,则按照 “第一声明优先” 原则进行仲裁,即选择 POM 中最先声明的版本。
注:合理使用 Maven 依赖仲裁机制可以便捷的管理 Jar 包版本而不合理的使用将导致多版本 Jar 冲突。
其次,JVM 类加载机制 决定了 Class 被加载到 JVM 的优先级, 如果同一个类出现在多个 Jar 包中,那么在 双亲委派类加载机制
下,加载该 Jar 包的类加载器层级越高,该 Jar 包越先被加载,它所包含的 Class 越先被执行,如上图所示:
- 启动类加载器(Bootstrap ClassLoader) 优先级最高,主要加载 JVM 运行时核心类,如 java.*等,这些类位于 $JAVA_HOME/lib/rt.jar 文件中
- 扩展类加载器(Extention ClassLoader) 优先级次之,主要加载 JVM 扩展类,如 swing 组件、xml 解析器等,这些类主要位于 $JAVA_HOME/lib/ext/ 目录下的 Jar 包中。
- 应用类加载器(Application ClassLoader) 又称系统类加载器,优先级再次之,它会加载 Classpath环境变量里定义的路径中的 Jar 包和目录,通常我们自己编写的代码或依赖的第三方 Jar 包都是由它来加载。
除了上述两种原因外,在同一个 ClassLoader 下,如果存在一个 Class 出现在不同的 Jar 包中,那么文件系统的文件加载顺序也可能会影响最终的加载结果。因此,应该尽量保证开发/测试/生产系统环境一致性。
那么如何解决?
下面是我这次解决版本冲突的方法步骤
1. 首先先确定下,看下控制台,缺少了那些类或者方法
-
通过网址(https://findjar.com/)确定是哪个jar包(如果知道那个jar包直接跳过 步骤 2 )
-
在通过idea中的maven依赖 show Dependencies 来查询jar版本是否异常,是否存在如果缺失 补上,存在就选择需要的版本,排除不需要的版本
一般由红线就是版本冲突
其他办法可以借鉴下,综合使用
或者看这个external libaraies查看是否由jar导入了多个版本 在排除(比较慢)
或者在打开项目的pom.xml文件中,可以直接查看冲突,和项目中使用的版本
本文介绍的 Jar 包冲突解决方法,除了解决 本文中的两个异常 以外,对其他相似问题也具备一定的参考价值
例如 java.lang.ClassNotFoundException,即加载不到指定类,通常是 Maven仲裁选错了版本
如本地开发阶段调用了 1.2.0 版本,而打包时采用了 1.0.0 版本的 Jar 包。同理,java.lang.NoClassDefFoundError 和 java.lang.LinkageError 也可以基于上述思路进行排查。 此外,如果类和方法名都保持不变,但是内部实现有变化,在多版本冲突场景下,不会抛出异常,但程序行为跟预期不一致, 此时,也可以基于上述思路进行排查诊断。