NoSuchMethodError 发生原因和解决办法
当应用程序试图调用类(静态或实例)的指定方法,而该类已不再具有该方法的定义时,就会抛出 java.lang.NoSuchMethodError 错误。简单地说,就是同一个 Class 有多个版本的实现,并且在运行时调用了缺少方法的那个版本。本文总结了 NoSuchMethodError 常见原因及其解决方法,如有遗漏或错误,欢迎补充指正。
运行时抛出 NoSuchMethodError 的根本原因是什么?
在实际生产系统中,我们主要关注运行时抛出的 NoSuchMethodError 错误,该错误轻则导致程序异常终止,严重时甚至会产生不可预知的程序结果,比如支付服务执行异常,实际支付已完成,却向用户返回支付失败。
运行时抛出 NoSuchMethodError 错误的根本原因就是:应用程序直接或间接依赖了同一个类的多个版本,并且在运行时执行了缺少方法的版本。如下图所示:
因此,核心问题就转化为:同一类为什么会有多个版本?哪个版本的类最终会被执行?
为什么同一个 Class 会出现多个版本?
导致 Java Class 出现多版本的原因,可以归纳为以下几类:
JDK 版本不一致。常见于编译打包环境使用高版本 JDK 开发与打包,而实际运行环境的 JDK 版本较低。例如,本地项目环境 JDK 版本为 1.7,调用 Character.isAlphabetic() 方法判断当前字符是否为字母;而线上环境 JDK 版本为 1.6,在运行期间就会抛出 NoSuchMethodError 错误。
SNAPSHOT 版本不一致。常见于本地更新 SNAPSHOT 版本后,没有执行 mvn clean deploy 部署,导致线上环境运行时仍然引用了旧版本的 SNAPSHOT 包。
Maven 依赖生命周期为 provided。常见于本地依赖的某组件生命周期为 provided,所声明版本仅用于本地编译打包,而线上运行时会通过其他依赖关系加载 Jar 包。
同一个 Jar 包出现了多个版本。常见于 Maven 依赖未显式指定版本号,导致间接依赖版本冲突,很容易引入低版本的 Jar 包。
同一个 Class 出现在不同的 Jar 包中。该问题常见于代码拷贝场景,比如基于开源版本定制了一些功能,使用了新的 Maven 坐标打包发布,此时 Maven 仲裁机制失效(非常隐蔽,难以排查)。由于 JVM 类加载器对于同一个类只会加载一次,最终加载的类实现受到 Jar 包依赖的路径、类声明的先后顺序或文件加载顺序等因素的影响,很可能出现不同机器加载的类实现不一致。
哪个版本的 Class 最终会被执行?
影响 Class 最终是否被执行的关键因素有两个:Maven 依赖仲裁机制和 JVM 类加载机制,如下图所示:
首先,Maven 依赖仲裁机制 决定了打包的优先级, 仲裁优先级“从高到低”如下所述:
优先按照依赖管理 [dependencyManagement] 元素中指定的版本进行仲裁;
若无版本声明,则按照 “短路径优先” 原则(Maven2.0)进行仲裁,即选择依赖树中路径最短的版本;
若路径长度一致,则按照 “第一声明优先” 原则进行仲裁,即选择 POM 中最先声明的版本。
合理使用 Maven 依赖仲裁机制可以便捷的管理 Jar 包版本,而不合理的使用将导致多版本 Jar 冲突。
其次,JVM 类加载机制 决定了 Class 被加载到 JVM 的优先级, 如果同一个类出现在多个 Jar 包中,那么在 双亲委派类加载机制 下,加载该 Jar 包的类加载器层级越高,该 Jar 包越先被加载,它所包含的 Class 越先被执行,如上图所示:
启动类加载器(Bootstrap ClassLoader)优先级最高,主要加载 JVM 运行时核心类,如 java.util、java.io等,这些类主要位于 $JAVA_HOME/lib/rt.jar 文件中。
扩展类加载器(Extention ClassLoader)优先级次之,主要加载 JVM 扩展类,如 swing 组件、xml 解析器等,这些类主要位于 $JAVA_HOME/lib/ext/ 目录下的 Jar 包中。
应用类加载器(Application ClassLoader),又称系统类加载器,优先级再次之,它会加载 Classpath 环境变量里定义的路径中的 Jar 包和目录,通常我们自己编写的代码或依赖的第三方 Jar 包都是由它来加载。
除了上述两种原因外,在同一个 ClassLoader 下,如果存在一个 Class 出现在不同的 Jar 包中,那么文件系统的文件加载顺序也可能会影响最终的加载结果。因此,应该尽量保证开发/测试/生产系统环境一致性。
如何解决 NoSuchMethodError 错误?
虽然抛出 NoSuchMethodError 错误的原因多种多样,但本质上是由于编译时类路径与运行时类路径不一致。因此,通用的定位思路可以归纳为以下 3 步:
1、定位异常 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 ……
2、定位异常 Class 的来源,可以通过 Arthas 等在线诊断工具反编译,如 jad com.xxx.AsyncAppender,获取该类运行时的源码、ClassLoader、Jar 包位置等信息。
1、根据 ClassLoader 和 Jar 包全路径名等信息,判断是类加载、Maven 仲裁或其他原因,并对应的加以解决。例如加载了同一个 Jar 包的低版本实现,则在 Maven dependencyManagement 中指定版本,或者移除间接依赖中的低版本(提示: 执行 mvn dependency:tree 命令,可以输出 Maven 依赖拓扑关系)。
其他 Jar 包冲突问题
本文介绍的 Jar 包冲突解决方法,除了解决 java.lang.NoSuchMethodError 以外,对其他相似问题也具备一定的参考价值。
例如 java.lang.ClassNotFoundException,即加载不到指定类,通常是 Maven 仲裁选错了版本,如本地开发阶段调用了 1.2.0 版本,而打包时采用了 1.0.0 版本的 Jar 包。同理,java.lang.NoClassDefFoundError 和 java.lang.LinkageError 也可以基于上述思路进行排查。
此外,如果类和方法名都保持不变,但是内部实现有变化,在多版本冲突场景下,不会抛出异常,但程序行为跟预期不一致, 此时,也可以基于上述思路进行排查诊断。
参考文章
重新看待 Jar 包冲突问题及解决方案
http://www.yangbing.club/2017/07/15/solution-for-jar-conflicts/
3 Steps to Fix NoSuchMethodErrors and NoSuchMethodExceptions
https://reflectoring.io/nosuchmethod/
作者信息:
夏明,GitHub ID @StabilityMan,花名涯海,阿里云 ARMS & EagleEye 技术专家,2016 年加入阿里巴巴,一直从事链路追踪和 APM 监控诊断领域的相关工作。
本文缩略图:icon by 轩坊
NoSuchMethodError 发生原因和解决办法相关推荐
- 你真的知道 NoSuchMethodError 发生原因和解决办法吗
点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"加入公众号专属技术群 来源:阿里巴巴中间件 当应用程序试图调用类(静态或实例)的 ...
- NoSuchMethodError 的发生原因和解决办法
当应用程序试图调用类(静态或实例)的指定方法,而该类已不再具有该方法的定义时,就会抛出 java.lang.NoSuchMethodError 错误.简单地说,就是同一个 Class 有多个版本的实现 ...
- 富士通打印机调整位置_打印机为什么卡纸 打印机四种卡纸原因及解决办法【介绍】...
我们在使用打印机的过程中,由于频繁使用打印机,常常会出现打印机无法处理纸张的故障.大多数常见的故障就是夹纸.不进纸.一次进多页纸以及乱走纸等.我们在打印机出现这些现象的时候,该怎样去应对呢?下面为大家 ...
- [J2ME]Nokia播放音乐时发生MediaException的解决办法
[J2ME]Nokia播放音乐时发生MediaException的解决办法 现象 在Nokia7610上播放音乐时遇到了-18的MediaException错误! J2ME中播放音乐资源的代码很容易 ...
- compress后的bytearray再decode变大_笔记本电脑风扇噪音变大的原因及其解决办法
笔记本使用时间长了之后,风扇声音会变大,那么如何再把风扇声音变小呢?怎么减小笔记本风扇的噪音?是什么原因导致笔记本风扇声音变大?下面给大家介绍笔记本风扇声音变大的原因及其解决办法. 原因: 1.使用时 ...
- 有关域索引错误产生的原因及解决办法
1说明 数据库错误ORA-29861:域索引标记为LOADING/FAILED/UNUSABLE,其错误原因及解决办法,根据ORACLE官方文档的说法如下: // *Cause: An attempt ...
- Session莫名丢失的原因及解决办法[转载]
Asp.net 默认配置下,Session莫名丢失的原因及解决办法 正常操作情况下Session会无故丢失.因为程序是在不停的被操作,排除Session超时的可能.另外,Session超时时间被设定成 ...
- mysql数据库什么情况下会锁表_mysql数据库锁的产生原因及解决办法
数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性 ...
- mysql数据库死锁的产生原因及解决办法
该文章为转载,如有侵权请及时联系 这篇文章主要介绍了mysql数据库锁的产生原因及解决办法,需要的朋友可以参考下 数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据 ...
最新文章
- 2017年度最受欢迎开源中国项目:roncoo-pay投票评选
- 如何使Layouts里的页面应用站点母板页
- Matrix PKU 2155
- c# 插入树形数据#_C#数据类型能力问题 套装1
- 11月24日 layouts and rendering in rails(部分没有看)
- creo打不开stp文件_让Creo输出的stp格式文件含有颜色设置 | 坐倚北风
- mysql续型_mysql续集(查询部分)
- 用Redis实现Session功能
- PHP文件流下载内容
- svn开发环境代码合并到生产
- windows 下载 gcc
- SVN下载项目到本地
- 学习计算机英语的重要性
- 记一次修复Mac和Win7双系统启动菜单的经历
- win10服务器怎么连接显示器不亮,win10开机后显示器不亮如何解决_win10开机后显示器不亮的处理方法...
- 2020线上夏令营感受
- Linux内核基础--事件通知链(notifier chain)good【转】
- 2020.3.23 bugku(21-25)
- r语言mfrow全程_R语言(绘图入门)
- linux中可以使用以下命令查看文件内容,在Linux服务器中使用命令行中查看文件内容...