一次诡异的
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 扫描"
bash
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
中显式排除:
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
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.taobao.notify</groupId>
<artifactId>notify-common</artifactId>
<version>1.8.19.26</version>
</dependency>
</dependencies>
</dependencyManagement>
3.3 全局分析:一次性导出完整树
bash
mvn dependency:tree -Dverbose > tree.log
用文本编辑器搜索冲突 groupId:artifactId
,快速定位所有叶子节点。
四、小结:一张图看懂排包流程
┌────────────┐ ┌──────────────┐ ┌────────────────┐
│ NoSuchM··· │ → │ dependency: │ → │ exclusion / │
│ LinkageErr │ │ tree -Dverbose│ │ dependencyMgmt │
└────────────┘ └──────────────┘ └────────────────┘
↑ ↑ ↑
运行报错 快速定位冲突 精准解决
一句话记住 :先
tree
找元凶,再exclusion
干掉它,最后用dependencyManagement
统一版本,冲突永不再现!