写点什么

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

作者:崔认知
  • 2022 年 9 月 04 日
    北京
  • 本文字数:1767 字

    阅读完需:约 6 分钟

首发:https://mp.weixin.qq.com/s?__biz=Mzg4MzcwMTk0Mw==&mid=2247484220&idx=1&sn=06ce29ba0eae325ad6fc1d9953571bb8&chksm=cf4222e4f835abf2a5927e09369142ae72444fc9b50e8ae09ca89242ea1e14b976da7800585f&token=990766686&lang=zh_CN#rd





现象



IDE 中编译运行没问题,但是打包成可运行 jar(spring boot jar 包运行)抛出异常:NoSuchMethodError 或 NoSuchFieldError 异常


如何定位问题



根据类加载机制判断,此类肯定被加载了,加载的到底是哪个类呢,因为本地 IDE 中可能没问题。


  • 理性判断:根据类加载机制


为了查看类的加载情况,我们可以应用启动时,增加 JVM 参数:

-verbose:class、

或-XX:+TraceClassLoading (旧版本 jdk)、

或-Xlog:class+load=debug(新版本 jdk,目前 jdk17 用的此配置)

https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#convert-runtime-logging-flags-to-xlog


本地模拟 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




<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId> <version>1.4.1</version> <executions> <execution> <id>enforce</id> <configuration> <rules> <dependencyConvergence/> </rules> </configuration> <goals> <goal>enforce</goal> </goals> </execution> <execution> <id>enforce-ban-duplicate-classes</id> <goals> <goal>enforce</goal> </goals> <configuration> <rules> <banDuplicateClasses> <ignoreClasses> <ignoreClass>javax.*</ignoreClass> <ignoreClass>org.junit.*</ignoreClass> <ignoreClass>net.sf.cglib.*</ignoreClass> <ignoreClass>org.apache.commons.logging.*</ignoreClass> <ignoreClass>org.springframework.remoting.rmi.RmiInvocationHandler</ignoreClass> </ignoreClasses> <findAllDuplicates>true</findAllDuplicates> </banDuplicateClasses> </rules> <fail>true</fail> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.codehaus.mojo</groupId> <artifactId>extra-enforcer-rules</artifactId> <version>1.0-beta-6</version> </dependency> </dependencies></plugin>
复制代码



如果你使用的是 Android Studio(Gradle 工具),很可能经常会碰到这类编译错误提示。


反射这种方式导致的,目前还没能自动检测工具。


小结




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



收录于合集 #避坑指南

 12 个

下一篇 Java 避坑指南:慎用 Lombok 代码自动生成工具

发布于: 刚刚阅读数: 6
用户头像

崔认知

关注

认知科技技术团队-微信公众号 2021.07.30 加入

认知科技技术团队

评论

发布
暂无评论
Java问题解决录: 运行时抛出NoSuchMethodError / NoSuchFieldError异常_崔认知_InfoQ写作社区