更新时间:2022-05-02 17:50:22
作者:涯海
创作日期:2019-08-06
专栏地址:【稳定大于一切】
当应用程序试图调用类(静态或实例)的指定方法,而该类已不再具有该方法的定义时,就会抛出 java.lang.NoSuchMethodError
错误。简单地说,就是同一个类有多个版本,并且在运行时调用了缺少方法的版本。
在实际生产系统中,我们主要关注运行时抛出的 NoSuchMethodError
错误,该错误轻则导致程序异常终止,严重时甚至会产生不可预知的程序结果,比如支付服务执行异常,实际支付已完成,却向用户返回支付失败。
运行时抛出 NoSuchMethodError
错误的根本原因就是: 应用程序直接或间接依赖了同一个类的多个版本,并且在运行时执行了缺少方法的版本。 如下图所示:
因此,核心问题就转化为: 同一类为什么会有多个版本?哪个版本的类最终会被执行?
导致 Java Class 出现多版本的原因,可以归纳为以下几类:
Character.isAlphabetic()
方法判断当前字符是否为字母;而线上环境 JDK 版本为 1.6,在运行期间就会抛出 NoSuchMethodError
错误。mvn clean deploy
部署,导致线上环境运行时仍然引用了旧版本的 SNAPSHOT 包。provided
,所声明版本仅用于本地编译打包,而线上运行时会通过其他依赖关系加载 Jar 包。影响 Class 最终是否被执行的关键因素有两个:Maven 依赖仲裁机制和 JVM 类加载机制,如下图所示:
首先,Maven 依赖仲裁机制 决定了打包的优先级, 仲裁优先级“从高到低”如下所述:
合理使用 Maven 依赖仲裁机制可以便捷的管理 Jar 包版本,而不合理的使用将导致多版本 Jar 冲突。
其次,JVM 类加载机制 决定了 Class 被加载到 JVM 的优先级, 如果同一个类出现在多个 Jar 包中,那么在 双亲委派类加载机制 下,加载该 Jar 包的类加载器层级越高,该 Jar 包越先被加载,它所包含的 Class 越先被执行,如上图所示:
java.util
、java.io
等,这些类主要位于 $JAVA_HOME/lib/rt.jar
文件中。swing 组件
、xml 解析器
等,这些类主要位于 $JAVA_HOME/lib/ext/
目录下的 Jar 包中。除了上述两种原因外,在同一个 ClassLoader 下,如果存在一个 Class 出现在不同的 Jar 包中,那么文件系统的文件加载顺序也可能会影响最终的加载结果。因此,应该尽量保证开发/测试/生产系统环境一致性。
虽然抛出 NoSuchMethodError
错误的原因多种多样,但本质上是由于编译时类路径与运行时类路径不一致。因此,通用的定位思路可以归纳为以下 3 步:
定位异常 Class 的全限定类名与调用方,通常可以在应用日志抛出的异常堆栈中获取。如下图所示:
Exception in thread "main" java.lang.NoSuchMethodError: com.xxx.AsyncAppender.append(Ljava/lang/String;)Ljava/lang/String;
at com.xxx.ProvokeNoSuchMethodError.main(ProvokeNoSuchMethodError:7)
at ……
定位异常 Class 的来源,可以通过 Arthas 等在线诊断工具反编译,如 jad com.xxx.AsyncAppender
,获取该类运行时的源码、ClassLoader、Jar 包位置等信息。
![image.png](https://ata2-img.cn-hangzhou.oss-pub.aliyun-inc.com/318bc9c65f88d24859641c7bbafd2a51.png)
mvn dependency:tree
命令,可以输出 Maven 依赖拓扑关系)。本文介绍的 Jar 包冲突解决方法,除了解决 java.lang.NoSuchMethodError
以外,对其他相似问题也具备一定的参考价值。
例如 java.lang.ClassNotFoundException
,即加载不到指定类,通常是 Maven 仲裁选错了版本,如本地开发阶段调用了 1.2.0 版本,而打包时采用了 1.0.0 版本的 Jar 包。同理,java.lang.NoClassDefFoundError
和 java.lang.LinkageError
也可以基于上述思路进行排查。
此外,如果类和方法名都保持不变,但是内部实现有变化,在多版本冲突场景下,不会抛出异常,但程序行为跟预期不一致, 此时,也可以基于上述思路进行排查诊断。
【稳定大于一切】打造国内稳定性领域知识库,让无法解决的问题少一点点,让世界的确定性多一点点。