NoSuchMethodError本质是运行时方法缺失,根源在于编译与运行加载的类版本不一致;需通过错误堆栈定位类、方法签名及调用位置,再用-verbose:class或Arthas查实际加载的JAR,结合dependency:tree分析并排除冲突依赖,最后清理重建验证。

编译能过,运行就炸——NoSuchMethodError正是这种“表面正常、实则脆弱”的典型表现。它不是代码写错了,而是运行时 JVM 找不到那个方法,根源几乎总是:**编译用的类和运行加载的类不是同一个版本**。
看懂错误信息里的关键三要素
错误堆栈里类似这样的行:
java.lang.NoSuchMethodError: com.google.common.collect.Lists.partition(Ljava/util/List;I)Ljava/util/List;
要立刻抓住三个信息:
- 类名(com.google.common.collect.Lists):定位是哪个库出的问题
- 方法名与签名(partition(Ljava/util/List;I)):括号里是参数类型,I 表示 int,不是 long;这说明哪怕只是参数类型变了(比如 Guava 31 把 int 改成 long),也会触发此错
- 调用位置(如 BaseDao.java:610):知道哪行代码触发了问题,方便回溯依赖链
确认运行时到底加载了哪个 Jar
别猜,直接查 JVM 实际加载的是谁:
- 启动时加
-verbose:class,运行后 grep 类名,例如:java -verbose:class -jar app.jar 2>&1 | grep Lists
输出类似[Loaded com.google.common.collect.Lists from file:/app/lib/guava-29.0-jre.jar],这就锁定了“真凶” - 更高效的方式是用 Arthas:
sc -d com.google.common.collect.Lists—— 一行命令返回类来源、ClassLoader、是否被重复定义 - Spring Boot fat jar?直接解压看
BOOT-INF/lib/下实际打包进去的是哪个版本
定位冲突源头:从 dependency:tree 到手动排除
Maven 的依赖树只反映编译期声明,但运行时可能被覆盖:
- 先跑:
mvn dependency:tree -Dincludes=com.google.guava,看有没有多个版本共存 - 重点检查
scope=provided的依赖:比如 Tomcat 提供的 servlet-api 或容器自带的 Guava,如果项目又显式引入另一个版本,极易冲突 - 对间接拉入的老版本,用
主动剔除:
比如某 starter 带来了 guava-29.0,而你需要 31.1,就在该 starter 的 dependency 块中排除它 - 警惕 shaded 包:Elasticsearch、Flink 等组件常把 commons-lang3、jackson 等打成内部包,外部再引同名库,签名可能不兼容
验证与收尾:确保一致性落地
改完不验证 = 白改:
- 清理构建产物:
mvn clean+ 删除本地target/和 IDE 缓存(IntelliJ 的 File → Reload project) - 检查最终 classpath:运行时用
jcmd $PID VM.system_properties | grep java.class.path,确认路径里没有残留旧 jar - 上线前在目标环境(如 Docker 镜像)里抽样验证 jar 包版本,避免“本地好,线上崩”










