依赖包冲突本质是运行时类加载顺序问题,需通过JVM实际加载路径、类加载器委托链、classpath顺序及字节码验证综合排查,而非仅看编译声明。

依赖包冲突不是编译期问题,而是运行时类加载阶段的真实行为偏差。排查关键不在“哪些包被声明”,而在“哪个类真被加载、由谁加载、从哪来”。
看实际加载的类来自哪个 JAR
不能只信 pom.xml 或 IDEA 提示,要问 JVM:
- 用 ProtectionDomain 获取路径:
clazz.getProtectionDomain().getCodeSource().getLocation(),返回如/app/lib/spring-core-5.3.30.jar - 若抛 SecurityException 或返回 null(常见于模块化或受限环境),fallback 到
clazz.getClassLoader().getResource("com/example/MyClass.class") - 解析 URL:以
jar:file:开头则提取!/前部分;以file:开头说明是 classes 目录直载
查加载器层级与委托链
同一类名在不同环境表现不一,常因类加载器委托顺序变化:
- 打印加载器链:
clazz.getClassLoader()→.getParent()→ 再.getParent(),直到为 null(即 Bootstrap) - 重点确认:该类是否本该由 Application ClassLoader 加载,却被 Extension 或 Bootstrap 提前加载(比如 jre/lib/ext 下有旧版 commons-logging)
- 检查启动参数:
System.getProperty("java.ext.dirs")和"java.class.path",看是否有意外 JAR 被塞入
验证运行时真实类路径顺序
JVM 不比较版本高低,只按 classpath 顺序“先到先得”:
立即学习“Java免费学习笔记(深入)”;
- IDEA 中:Project Structure → Modules → Dependencies,观察 jar 列表上下顺序(越靠上优先级越高)
- Eclipse 中:Properties → Java Build Path → Order and Export,注意勾选顺序
- 启动时加
-verbose:class,日志中找Loaded com.example.SomeClass from file:/xxx/xxx.jar,关注报错类首次出现的位置
定位冲突类并比对字节码内容
报错只是表象,必须确认方法是否存在、签名是否匹配:
- 用 Arthas 执行
sc -d com.example.SomeService,看它由哪个 ClassLoader、从哪个 JAR 加载 - 执行
sm com.example.SomeService methodName,列出当前已加载类中实际存在的方法,确认缺失方法是否真的不存在 - 若发现
spring-core-5.2.9.jar和spring-core-5.3.30.jar都在 classpath,而调用的方法仅在 5.3.30 中存在,但加载的是 5.2.9 的类,就是典型加载顺序错配


















