nosuchmethoderror是运行时错误,因jvm在调用方法时发现类中不存在对应签名,常见于编译与运行时guava等库版本不一致,如编译用31.1而运行用29.0导致lists.partition()缺失。

为什么NoSuchMethodError不是编译报错,运行才炸?
因为这个错误发生在类加载后、方法调用时——JVM发现目标类里真没有那个方法签名。常见于编译时用的是A版本的jar(比如guava-31.1-jre.jar),运行时却加载了B版本(比如guava-29.0-jre.jar),而B版本删了/改了/重命名了你调用的Lists.partition()这类方法。
- 编译通过 ≠ 运行安全:IDE或Maven只校验编译期依赖,不保证运行时类路径干净
- 错误信息里
NoSuchMethodError: com.google.common.collect.Lists.partition这种,重点看类名+方法名+参数类型(比如(Ljava/util/List;I)Ljava/util/List;) - 它和
NoClassDefFoundError不同:后者是整个类找不到;这个是类找到了,但方法没了
怎么快速定位到底是哪个jar在搞鬼?
别猜,直接查运行时实际加载的类来自哪:
- 启动时加JVM参数:
-verbose:class,然后grep关键词,比如grep Lists,能看到类似[Loaded com.google.common.collect.Lists from file:/app/lib/guava-29.0-jre.jar] - 或者用
jcmd+jstack组合:先jcmd查PID,再jcmd $PID VM.native_memory summary辅助判断,但更准的是用Arthas:sc -d com.google.common.collect.Lists,它会直接告诉你类从哪个jar加载、是否被重复定义 - Maven项目可跑:
mvn dependency:tree -Dincludes=com.google.guava,但注意这只是编译依赖树,运行时可能被provided或shaded覆盖
mvn dependency:tree看到多个版本,删哪个?
不是删“旧的”,而是删“不该出现的”:
- 看
scope:如果某个guava标记为provided(比如由容器提供),但你的应用又显式引入了另一个版本,就容易冲突 - 看
optional和exclusion:某些框架(如spring-boot-starter-web)会间接拉进tomcat-embed-core,它可能带老版guava,这时要在该依赖下用<exclusions></exclusions>干掉它 - 检查
lib/目录或BOOT-INF/lib/(Spring Boot fat jar):手动丢进去的jar优先级高于Maven声明,哪怕版本低也会胜出 - 注意
shaded包:比如elasticsearch-rest-high-level-client自己shade了commons-lang3,如果你另外引了同名库,方法签名可能对不上
修复后为什么还是报错?检查这三处
- 类加载器隔离没打破:Web应用在Tomcat里,
WEB-INF/lib里的jar由WebAppClassLoader加载,而lib/下的是SharedClassLoader,两者不共享,方法不可见
- 方法签名细微差异:比如你以为调的是
partition(List, int),但实际编译用了partition(List, long)(Guava 31改过),而运行时只有int版,JVM认为这是两个方法
- JVM缓存了旧类定义:改完
jar后没清-XX:ReservedCodeCacheSize或没重启,尤其热部署场景下,旧字节码可能还钉在元空间里
WEB-INF/lib里的jar由WebAppClassLoader加载,而lib/下的是SharedClassLoader,两者不共享,方法不可见 partition(List, int),但实际编译用了partition(List, long)(Guava 31改过),而运行时只有int版,JVM认为这是两个方法 jar后没清-XX:ReservedCodeCacheSize或没重启,尤其热部署场景下,旧字节码可能还钉在元空间里 真正麻烦的从来不是找不到错在哪,而是同一个jar名、不同构建方式(maven-shade、gradle-shadow、手动zip)导致内部包路径、签名、甚至MANIFEST.MF里的Class-Path都不同,得一层层扒字节码确认。
立即学习“Java免费学习笔记(深入)”;










