核心是确认“哪个类从哪个jar加载”,而非猜测版本或盲目删包;jvm按类路径顺序“先到先得”,通过-verbose:class或arthas的sc -d命令可精准定位运行时实际加载来源,结合dependencymanagement锁定版本、exclusions排除冲突依赖,从构建期根治问题。

核心是搞清“哪个类从哪个 jar 加载上来”,而不是猜版本或删包。JVM 类加载走的是“先到先得”路径,只要确认了实际加载来源,冲突根源就浮出水面。
查运行时真实加载路径
启动时加 -verbose:class 参数,JVM 会打印每一类的加载来源。重点关注报错类第一次出现的位置,例如:
- [Loaded org.springframework.core.io.Resource from file:/app/lib/spring-core-5.2.9.jar]
- [Loaded com.fasterxml.jackson.databind.ObjectMapper from file:/app/lib/jackson-databind-2.11.0.jar]
如果报 NoSuchMethodError 却显示加载的是旧版 jar,说明它被提前命中了——后续同名新版本根本没机会入场。
用 Arthas 快速定位线上问题
无需重启、不改代码,直接在运行中查:
- sc -d com.xxx.Yyy:列出该类被哪个 ClassLoader、从哪个 jar 加载
- sm com.xxx.Yyy *:查看该类当前实际定义了哪些方法,和文档比对是否缺方法
- classloader -t:看类加载器层级关系,判断是否被容器(如 Tomcat)的共享类加载器抢先加载
比如发现 Resource 被 ParallelWebappClassLoader 加载自 spring-web-4.3.27.jar,而项目依赖的是 5.3.x,基本可断定是 web 容器自带老版本干扰。
控制加载顺序的关键操作
不是靠“优先级”,而是靠“谁排在前面”:
- IDE 中:IntelliJ → Project Structure → Modules → Dependencies,把目标 jar 拖到列表顶部;Eclipse → Build Path → Order and Export,勾选并上移
- 打包后(如 Spring Boot fat jar):解压
BOOT-INF/lib/,jar 文件名按字母序加载;可重命名如001-spring-core-5.3.30.jar确保靠前 - Web 应用:把关键 jar 放进
WEB-INF/lib/,同样用数字前缀控制顺序;避免放在Tomcat/lib/这类共享目录
构建期就堵住源头
运行时排查是补救,构建期约束才是根治:
- 用
<dependencymanagement></dependencymanagement>锁定 BOM 版本,例如spring-framework-bom,统一所有子模块的 spring 相关依赖 - 对已知冲突的传递依赖,显式
<exclusions></exclusions>排除,不给它混入机会 - 执行
mvn dependency:tree -Dverbose,识别重复引入路径,把更合理的依赖声明提到 pom 靠前位置(Maven 按声明顺序仲裁)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











