JAR 包冲突排雷指南:原理、现象与 Maven 一站式解决
一次诡异的
NoSuchMethodError
,90 % 都是由同一个类在多个版本间反复横跳引起的。
一、为什么会冲突?
1.1 冲突产生的根因:同一个类在 classpath 中出现多次,且版本不同
以开篇例子为例:
a.jar
→ 引入c.jar(version-1)
b.jar
→ 引入c.jar(version-2)
项目运行时 “先到先得” 的类加载顺序导致:
加载顺序 | 结果 |
---|---|
先加载 c-1 | 低版本类 ClassA 缺失 methodA() → NoSuchMethodError |
先加载 c-2 | 高版本向下兼容,一切正常 |
1.2 典型异常对照表
异常 | 含义 |
---|---|
java.lang.ClassNotFoundException | 版本里根本没有这个类 |
java.lang.NoSuchMethodError | 版本里有类,但没有这个方法 |
java.lang.NoSuchFieldError | 版本里有类,但没有这个字段 |
java.lang.LinkageError | 类加载冲突导致链接失败 |
二、排查思路:三步定位法
Step 1:打印依赖树 —— 用 Maven 做“CT 扫描”
mvn dependency:tree -Dverbose -Dincludes=:notify-common
输出示例(已高亮冲突):
[INFO] +- com.taobao.wlb:bis-core:jar:1.0-SNAPSHOT
[INFO] | \- com.taobao.logistics:schedule-client:1.1.1
[INFO] | \- (com.taobao.notify:notify-common:1.8.15 - omitted for conflict with 1.8.19.26)
[INFO] \- com.taobao.notify:notify-tr-client:1.8.19.26
[INFO] \- com.taobao.notify:notify-common:1.8.19.26
- 冲突版本:
1.8.15
vs1.8.19.26
- 谁是引入者:
schedule-client:1.1.1
拉来了旧版本。
Step 2:运行时类加载详情 —— JVM 的“X 光片”
启动参数加 -verbose:class
,查看实际从哪个 JAR 加载:
[Loaded com.xxx.ClassA from file:/.../c-1.0.jar]
Step 3:IDE 可视化 —— 适合“肉眼”快速定位
- Eclipse:打开
pom.xml → Hierarchy → 搜索冲突包
- IDEA:
pom.xml → Diagrams → Show Dependencies
三、解决手段:让“对的版本”留下来
3.1 排除旧版本(Maven)
找到引入冲突的坐标,在 pom.xml
中显式排除:
<dependency><groupId>com.know.diamond</groupId><artifactId>diamond-sdk</artifactId><version>2.0.5</version><exclusions><exclusion><groupId>com.google.collections</groupId><artifactId>google-collections</artifactId></exclusion></exclusions>
</dependency>
3.2 统一版本:使用 dependencyManagement
<dependencyManagement><dependencies><dependency><groupId>com.taobao.notify</groupId><artifactId>notify-common</artifactId><version>1.8.19.26</version></dependency></dependencies>
</dependencyManagement>
3.3 全局分析:一次性导出完整树
mvn dependency:tree -Dverbose > tree.log
用文本编辑器搜索冲突 groupId:artifactId
,快速定位所有叶子节点。
四、小结:一张图看懂排包流程
┌────────────┐ ┌──────────────┐ ┌────────────────┐
│ NoSuchM··· │ → │ dependency: │ → │ exclusion / │
│ LinkageErr │ │ tree -Dverbose│ │ dependencyMgmt │
└────────────┘ └──────────────┘ └────────────────┘↑ ↑ ↑运行报错 快速定位冲突 精准解决
一句话记住:先
tree
找元凶,再exclusion
干掉它,最后用dependencyManagement
统一版本,冲突永不再现!