Java 问题解决录: 运行时抛出 NoSuchMethodError / NoSuchFieldError 异常

现象
IDE 中编译运行没问题,但是打包成可运行 jar(spring boot jar 包运行)抛出异常:NoSuchMethodError 或 NoSuchFieldError 异常。
如何定位问题
根据类加载机制判断,此类肯定被加载了,加载的到底是哪个类呢,因为本地 IDE 中可能没问题。
理性判断:根据类加载机制
为了查看类的加载情况,我们可以应用启动时,增加 JVM 参数:
-verbose:class、
或-XX:+TraceClassLoading (旧版本 jdk)、
或-Xlog:class+load=debug(新版本 jdk,目前 jdk17 用的此配置);
本地模拟 IDE 启动添加参数:


当然,如果我们服务器使用了阿里开源的 Arthas,可以使用 jad 命令来查看反编译指定已加载类的源码:
https://arthas.aliyun.com/doc/jad.html#反编译时指定-classloader
以上工具可以查看类的加载路径及 jar 包版本号,而且还能看到使用的类加载器。
人肉判断
1、IDE 搜此类,如果定义出现在两个 jar 包,基本就是重复类定义导致的;
2、IDE 搜此类,如果出现在一个 jar 包,看 maven 依赖树,是否存在多个版本,定义是否相同,版本是否被覆盖(高版本覆盖低版本、低版本覆盖高版本);
根据 maven 的传递依赖规则,或者直接解压打包的应用,看最终打进去的 jar 包版本,或登录服务器查看;
3、看代码,是否反射,定义写错导致的;
运行时抛出 NoSuchMethodError / NoSuchFieldError 异常问题原因分类
1、重复类导致的;
同包同名类,实现不一样,类加载器只加载第一个找到的,而使用的类恰恰是有问题的。同名同包类可能在一个第三方 jar 包中,但大多数位于两个 jar 包中。
2、传递依赖多个版本冲突-最终生效的只有一个版本;
根据 maven 的传递依赖特性,maven 根据广度优先遍历算法来决定使用哪个冲突的版本号。有可能是高版本覆盖了低版本,也有可能是低版本覆盖了高版本。
3、反射机制,定义传递错误;
如何编译期发现
如果项目使用的是 maven 工具,我们可以使用 extra-enforcer-rules 中的
Ban Duplicate Classes 规则来强制编译报错。
https://www.mojohaus.org/extra-enforcer-rules/banDuplicateClasses.html
如果你使用的是 Android Studio(Gradle 工具),很可能经常会碰到这类编译错误提示。
反射这种方式导致的,目前还没能自动检测工具。
小结

,
脑图:公众号回复【NoSuchMethodError】获取

收录于合集 #避坑指南
12 个
下一篇 Java 避坑指南:慎用 Lombok 代码自动生成工具
版权声明: 本文为 InfoQ 作者【崔认知】的原创文章。
原文链接:【http://xie.infoq.cn/article/3fb91b472c87079f3a2f70e07】。文章转载请联系作者。
评论