惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
J
Java Code Geeks
博客园 - 聂微东
量子位
C
Check Point Blog
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
B
Blog
罗磊的独立博客
腾讯CDC
GbyAI
GbyAI
博客园 - 【当耐特】
A
About on SuperTechFans
M
MIT News - Artificial intelligence
U
Unit 42
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队

博客园 - JackYang

I walk alone句子结构中 alone 后置的原因及同类用法解析 《用了3个月Cursor后,我删掉了自己写了10年的工具库》:从“造轮子自豪”到“承认AI写得更好”的心理崩塌与重建 2026年AI Agent时代下如何打造新一代研发体系:从熵增困境到智能工厂的范式重构(基于2025年底文章重写) AgentScope-Java 深度实践:构建企业级生产就绪的智能体应用 传统软件的AI重生:大模型时代下的系统重构与智能升级(2026实践指南)——从代码理解到数据交互,构建安全、高效、可落地的企业级AI集成架构 浅析英文复合名词“X Recording / X Logging”的翻译逻辑与本地化策略——兼论“How + 复合名词 + Works”句式的处理 浅析英文句式“How + 名词 + Works”的翻译逻辑与应用场景 会员契约论新观察:从身份标签到价值博弈——解构与评估现代会员体系 深度剖析akshare-data:OpenClaw生态中的金融数据Skill构建之道——手把手教你用akshare-data搭建个人AI量化系统 DeepSeek-V4:中国大模型的新范式革命—— 万字深度技术全景解析 深入 Java I/O 核心:BufferedInputStream 全景式源码解析与工程实践——2026 高并发时代下的性能基石,从 JDK 源码到虚拟线程(Project Loom)的协同优化 2026年前端工程化的新纪元:从Rust工具链革命到AI Agent原生架构演进 万字详文:解构 SessionBindingService —— AI智能体的“会话粘合剂” Windows 本地部署 Hermes Agent 安装教程 + 飞书接入,会自我进化AI Agent 智能体(全程避坑,亲测有效) 2026年最新、最全、可用的Docker 国内镜像源加速(截至 2026 年 4月14日 亲测可用) 如何自定义Spring Boot的错误页面显示? Spring Boot的全局异常处理器如何配置? This application has no explicit mapping for /error “no main manifest attribute, in app.jar” 错误详解与解决方案 深度拆解三大 AI 核心协议:MCP、ACP、A2A,读懂智能代理全域通信底层逻辑—— 从技术原理、架构设计、落地场景到生态未来,全面揭秘 AI 智能体协作的底层标准 The Java® Virtual Machine Specification Java SE 26 Edition 中文版简介第二小节:The Java Virtual Machine The Java® Virtual Machine Specification Java SE 26 Edition 中文版简介第一小节:A Bit of History Java 中 “Error: Could not find or load main class” 错误详解与全面解决方案 云原生时代,Docker和虚拟机谁才是王者?K8s架构下的终极选型指南 2026年全网最新、最全、最好用的Excel快捷键大全:提升工作效率的必备技巧 2026年全网最新、最全的Word快捷键大全:从入门到精通 WPS快捷键大全及使用技巧 2026年最新版IntelliJ IDEA 2026.1下载、安装与配置教程: IDEA 2026.1新手使用教程(万字详解) OpenClaw源码大揭秘:WhatsApp登录工具如何用50行代码破解AI智能体的移动端困局? 2026AI Agent:AI Agent从“工具箱”到“操作系统”——深度解构AI聚合化(Aggregation)、自动化(Automation)与普惠化(Democratization
Java 中Jar包冲突问题及解决方案
JackYang · 2026-04-13 · via 博客园 - JackYang

Jar 包冲突是 Java 开发中最常见、最棘手的运行时问题之一。它通常在编译时正常,但在运行时报错,导致程序崩溃或行为异常。


🔍 一、Jar 包冲突的表现(典型异常)

当你遇到以下异常时,极有可能是 Jar 包冲突:

异常类型 说明
java.lang.NoClassDefFoundError 找不到某个类(但该类在编译时存在)
java.lang.NoSuchMethodError 找不到某个方法(方法签名存在,但实际没有)
java.lang.NoSuchFieldError 找不到某个字段
java.lang.IncompatibleClassChangeError 类结构不兼容(如接口变类、静态变非静态)
java.lang.LinkageError 类加载器加载了不兼容的类版本

💡 关键特征

  • 代码能正常编译打包
  • 运行时突然报错,且错误类“明明存在”
  • 错误信息指向第三方库中的类(如 com.fasterxml.jackson, org.apache.commons 等)

🧠 二、冲突的根本原因

1. 同一类被多个 Jar 包包含

  • 例如:commons-lang3-3.4.jarcommons-lang3-3.9.jar 同时存在
  • JVM 只会加载第一个找到的类,后续同名类被忽略
  • 如果先加载的是旧版本,而代码依赖新版本的方法 → NoSuchMethodError

2. Maven/Gradle 依赖传递导致版本不一致

<!-- A 依赖 commons-lang3:3.4 -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>lib-a</artifactId>
    <version>1.0</version>
</dependency>

<!-- B 依赖 commons-lang3:3.9 -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>lib-b</artifactId>
    <version>1.0</version>
</dependency>

→ 最终项目中可能只保留一个版本(由 Maven 的“最近优先”策略决定),但你的代码可能依赖另一个版本的特性。

3. 不同 Jar 包包含相同全限定名的类(包名+类名完全相同)

  • 某些厂商会“复制”开源代码并修改(如阿里 Fastjson vs Jackson)
  • 或使用 shade 打包时未重命名(如 Hadoop 生态常见)

🛠️ 三、排查方法

✅ 方法 1:使用 Maven 命令查看依赖树

# 查看整个依赖树
mvn dependency:tree

# 查找特定 Jar 的依赖路径
mvn dependency:tree -Dincludes=commons-lang3

# 输出到文件便于分析
mvn dependency:tree -DoutputFile=deps.txt

示例输出:

[INFO] +- com.example:lib-a:jar:1.0:compile
[INFO] |  \- org.apache.commons:commons-lang3:jar:3.4:compile
[INFO] \- com.example:lib-b:jar:1.0:compile
[INFO]    \- org.apache.commons:commons-lang3:jar:3.9:compile

→ 虽然两个版本都声明了,但最终只会保留一个(通常是路径最短最先声明的)。

✅ 方法 2:检查最终打包的 Jar/WAR 中是否包含重复类

# 解压你的 fat jar
unzip -l your-app.jar | grep "CommonUtils.class"

# 或使用工具
jar -tf your-app.jar | grep "\.class$" | sort | uniq -d

✅ 方法 3:运行时打印类加载路径(调试用)

System.out.println(
    StringUtils.class.getProtectionDomain().getCodeSource().getLocation()
);

→ 可看到实际加载的是哪个 Jar 包。


✅ 四、解决方案(按优先级排序)

🔧 方案 1:依赖排除(最常用)

pom.xml 中排除冲突的传递依赖:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>lib-a</artifactId>
    <version>1.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </exclusion>
    </exclusions>
</dependency>

🔧 方案 2:统一依赖版本(推荐)

<dependencyManagement> 中强制指定版本:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

→ 所有子模块都会使用这个版本。

🔧 方案 3:使用 Maven Enforcer 插件(预防)

pom.xml 中加入插件,禁止版本冲突:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <executions>
        <execution>
            <id>enforce</id>
            <configuration>
                <rules>
                    <dependencyConvergence/>
                </rules>
            </configuration>
            <goals>
                <goal>enforce</goal>
            </goals>
        </execution>
    </executions>
</plugin>

→ 构建时若发现同一 artifact 有多个版本,直接失败!

🔧 方案 4:重命名包(Shading,终极方案)

适用于无法控制依赖来源的场景(如 Flink、Spark 作业):

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals><goal>shade</goal></goals>
            <configuration>
                <relocations>
                    <relocation>
                        <pattern>com.google.common</pattern>
                        <shadedPattern>my.shaded.com.google.common</shadedPattern>
                    </relocation>
                </relocations>
            </configuration>
        </execution>
    </executions>
</plugin>

→ 将冲突的类重命名,彻底隔离。

🔧 方案 5:调整类加载器(高级,慎用)

  • 在 OSGi、Tomcat、WebLogic 等环境中,可通过配置类加载顺序解决
  • 例如 Tomcat 的 context.xml 中设置 loader 优先级

🚫 五、常见误区

误区 正确做法
“只要能编译通过就没问题” 冲突是运行时问题,编译无法发现
“把所有依赖都打进去最安全” 反而更容易引发冲突
“删掉一个 Jar 就行” 可能导致其他依赖缺失,应通过依赖管理解决

✅ 六、最佳实践

  1. 定期执行 mvn dependency:analyze,清理无用依赖
  2. 使用 dependencyManagement 统一版本
  3. 在 CI 流程中加入 maven-enforcer-plugin
  4. 避免手动拷贝 Jar 包到 lib 目录(破坏依赖管理)
  5. 对核心库(如 Jackson、Guava、Log4j)严格锁定版本

🔚 总结

场景 推荐方案
简单项目,少量依赖 依赖排除 + 统一版本
大型微服务项目 dependencyManagement + Enforcer 插件
大数据作业(Flink/Spark) Shading(重命名包)
无法修改依赖源码 调整类加载顺序或使用自定义 ClassLoader

💡 记住:Jar 包冲突的本质是 “同一个类,多个实现”。解决的核心思路是 “确保运行时只有一个正确版本被加载”

如果你提供具体的错误日志和 pom.xml 片段,我可以帮你精准定位冲突源!