如何解决版本不兼容Jar包冲突问题
如何解决版本不兼容Jar包冲突问题
引言
-
“老婆”和“妈妈”同时掉进水里,先救谁?
-
常言道:编码五分钟,解冲突两小时。作为Java开发来说,第一眼见到ClassNotFoundException、
-
NoSuchMethodException这些异常来说,第一反应就是排包。经过一通常规和非常规操作以后,往往会找到同一个Jar包引入了多个不同的版本,这时候一般排除掉低版本、保留高版本就可以了,这是因为一般Jar包都是向下兼容的。但是,如果出现版本不兼容的情况的时候,就会陷入“老婆和妈同时掉进水里,先救谁”的两难境地,如果恰恰这种不兼容发生在中间件依赖和业务自身依赖之间,那就更难了。
如下图所示,Project表示我们的项目,Dependency A表示我们的业务依赖,Dependency B表示中间件依赖,如果业务依赖和中间件依赖都依赖同一个Jar包C,但是版本却不一样,分别为0.1版本和0.2版本,而且最不巧的是这两个版本还存在冲突,有些老的功能只在0.1低版本中存在,有些新功能只在0.2高版本中存在,真是“老婆和妈同时掉进水里,先救谁都不行”。 -
-
俗话说:没有遇到过Jar包冲突的开发,一定是个假Java开发;没有解决过Jar包冲突的开发,不是一个合格的Java开发。在最近的项目里,我们需要使用Guava的高版本Jar包,但是发现中间件依赖的是低版本且与高版本不兼容的Jar包,面对这种两难,我们肯定是“老婆”和“妈妈”都要救,于是我们开始寻求解决方案。
不兼容依赖冲突解决方案
-
“老婆”和“妈妈”都要救,怎么救?
-
首先,我们想到的是,能不能把需要用到的Guava高版本的代码拷出来直接放到我们的工程中去,但是这样做会带来几个问题:
-
1)Guava作为一个功能丰富的基础库,某一部分的代码往往与其他很多代码都存在依赖关系,这会造成牵一发而动全身,工作量会比预想的要大很多;
-
2)拷贝出来的代码只能自己手动维护,如果官方修复了问题或者重构了代码或者增加了功能,我们想要升级的话,那么只能重头再来一遍。于是,我们只能另外想其他的方案,这个只能作为最后的兜底方案。
-
然后,我们在想,一个Java类被加载到JVM虚拟机里区别于另一个Class,其一是它们俩全路径不一样,是风马牛不相及的两个不同的类,但却是被不同的类加载器加载的,在JVM虚拟机里它们仍然被认为是两个不同的Class。所以,我们就在想从类加载器上来寻求解决方案。在阿里巴巴内部,有一个Pandora的组件,正如其名就像一个魔盒,它会把中间件的依赖都装到Pandora里(内部叫做Sar包),这样的话,就能避免在中间件和业务代码直接出现“老婆和妈同时掉进水里,先救谁”的两难境地。
-
同样,在类似的场景比如应用合并部署也能发挥威力。但是Pandora只在阿里内部使用并未开源。在蚂蚁金服,也有一个这样的组件,并且开源了,叫做SOFAArk(官方网址,感兴趣的可以去官网了解SOFAArk的原理和使用),我们感觉已经找到了那个Mr.Right,于是我们开始研究SOFAArk如何使用。和Pandora一样,SOFAArk也是通过使用不同的 ClassLoader 加载不同版本的三方依赖,进而隔离类,彻底解决包冲突的问题,这就要求我们需要将相关的依赖打包成Ark Plugin(参见SOFAArk官方文档)。
-
对于公司来说,这样的方案收益是比较大的,打包成Ark Plugin后整个公司都能够共享,业务方都能受益,但是对于我们一个项目来说,采用这样的方案无疑过重了。于是,我们与中间件同学联系,询问是否有计划引入类似的隔离组件解决中间件和业务代码之间的依赖冲突问题,得到的答复是公司目前包冲突并不是一个强烈的痛点,暂时没有计划引入。于是,我们只能暂且搁置SOFAArk,继续寻找新的解决方案。\
-
-
接着,我们在想既然Pandora/SOFAArk采用类加载隔离了同一路径的类,那么如果我们把冲突的两个版本库的groupId变得不一样,那么即使同名的类全路径也是不一样的,这样在JVM里面必然是不同的Class。如果把Pandora/SOFAArk的隔离方式称之为逻辑隔离的话,这种就相当于物理隔离了。要实现这一点,借助IDE的重构功能或者全局替换的功能就能比较容易的实现这一点。
正在我们准备撸起袖子动手干的时候,我们不禁在想,这样的痛点应该早就有人遇到,尤其像Guava、Commons这类的基础类库,冲突在所难免,前人应该已经找到了优雅的挠痒姿势。于是,我们就去搜索相关的文章,果不其然,maven-shade-plugin正是那优雅的挠痒姿势,这个Maven插件的原理正是将类的包路径进行重新映射,达到隔离不兼容Jar包的目的。
maven-shade-plugin解决依赖冲突**
-
最后如何来配置和使用maven-shade-plugin将Guava映射成我们自己定制的Jar包,实现与中间件Guava的隔离。整个的过程还是比较清晰明了的,主要是创建一个Maven工程,引入依赖,配置我们要发布的仓库地址,引入编译打包插件和maven-shade-plugin插件,配置映射规则(标签之间部分),然后编译打包发布到Maven仓库。pom.xml的配置如下:
-
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.shaded.example</groupId><artifactId>guava-wrapper</artifactId><version>${guava.wrapper.version}</version><name>guava-wrapper</name><url>https://example.com/guava-wrapper</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!- 版本与 guava 版本基本保持一致 -><guava.wrapper.version>27.1-jre</guava.wrapper.version><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>27.1-jre</version></dependency> </dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.3</version><configuration><source>${maven.compiler.source}</source><target>${maven.compiler.target}</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>2.3.2</version><executions><execution><id>default-jar</id><goals><goal>jar</goal></goals><phase>package</phase></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>2.4</version><executions><execution><id>default-sources</id><goals><goal>jar-no-fork</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>2.4.1</version><configuration><createDependencyReducedPom>false</createDependencyReducedPom></configuration><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><!-- 重命名规则配置 --><relocations><relocation><!-- 源包路径 --><pattern>com.google.guava</pattern><!-- 目标包路径 --><shadedPattern>com.google.guava.wrapper</shadedPattern></relocation><relocation><pattern>com.google.common</pattern><shadedPattern>com.google.common.wrapper</shadedPattern></relocation><relocation><pattern>com.google.thirdparty</pattern><shadedPattern>com.google.wrapper.thirdparty</shadedPattern></relocation></relocations><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/></transformers></configuration></execution></executions></plugin></plugins></build><distributionManagement><!- Maven仓库配置,略 -></distributionManagement> </project>
-
项目引入这个新打包的guava-wrapper后,import选择从这个包导入我们需要的相关类即可。如下:
-
<dependency><groupId>com.vivo.internet</groupId><artifactId>guava-wrapper</artifactId><version>27.1-jre</version> </dependency>
结语
- 为了在同一个项目中使用多个版本不兼容的Jar包,我们首先想到手动自行维护代码,但是工作量和维护成本很高,接着我们想到通过类加载器隔离(开源方案SOFAArk),但是需要将相关依赖都打包成Ark Plugin,解决方案无疑有点过重了,最后通过maven-shade-plugin插件重命名并打包,优雅地解决了项目中不兼容多个版本Jar包的冲突问题。从问题出来,我们一步一步探寻问题的解决方案,最终的maven-shade-plugin插件方案虽然看似与手动自行维护代码本质一致,看似回到了原点,但其实最终的方案优雅性远比最开始高得多,正如人生的道路那样,螺旋式上升,曲线式前进。
- 如果遇到类似需要支持版本不兼容Jar包共存的场景,可以考虑使用maven-shade-plugin插件,这种方法比较轻量级,可用于项目中存在个别不兼容Jar包冲突的场景,简单有效,成本也很低。但是,如果Jar包冲突现象比较普遍,已成为明显或者普遍的痛点,还是建议考虑文中提到的类似Pandora、SOFAArk等类加载器隔离的方案。
相关文章:

如何解决版本不兼容Jar包冲突问题
如何解决版本不兼容Jar包冲突问题 引言 “老婆”和“妈妈”同时掉进水里,先救谁? 常言道:编码五分钟,解冲突两小时。作为Java开发来说,第一眼见到ClassNotFoundException、 NoSuchMethodException这些异常来说&…...

数据结构—归并排序-C语言实现
引言:归并排序跟快速排序一样,都运用到了分治的算法,但是归并排序是一种稳定的算法,同时也具备高效,其时间复杂度为O(N*logN) 算法图解: 然后开始归并: 就是这个思想,拆成最小子问题…...

Multiple CORS header ‘Access-Control-Allow-Origin‘ not allowed
今天在修改天天生鲜超市项目的时候,因为使用了前后端分离模式,前端通过网关统一转发请求到后端服务,但是第一次使用就遇到了问题,比如跨域问题: 但是,其实网关里是有配置跨域的,只是忘了把前端项…...

msvcp100.dll丢失怎样修复,msvcp100.dll丢失问题全面解析
msvcp100.dll是一个动态链接库文件,属于 Microsoft Visual C Redistributable 的一个组件。它包含了 C 运行时库,这些库在运行程序时会被加载到内存中。msvcp100.dll文件的主要作用是为基于 Visual C 编写的程序提供必要的运行时支持。 当您运行一个基于…...

最新AI智能问答系统源码/AI绘画系统源码/支持GPT联网提问/Prompt应用+支持国内AI提问模型
一、AI创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的AI智能问答系统和AI绘画系统。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图…...

全连接网络实现回归【房价预测的数据】
也是分为data,model,train,test import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optimclass FCNet(nn.Module):def __init__(self):super(FCNet,self).__init__()self.fc1 nn.Linear(331,200)s…...

mysql八股
1、请你说说mysql索引,以及它们的好处和坏处 检索效率、存储资源、索引 索引就像指向表行的指针,是一个允许查询操作快速确定哪些行符合WHERE子句中的条件,并检索到这些行的其他列值的数据结构索引主要有普通索引、唯一索引、主键索引、外键…...
MATLAB算法实战应用案例精讲-【优化算法】狐猴优化器(LO)(附MATLAB代码实现)
代码实现 MATLAB LO.m %======================================================================= % Lemurs Optimizer: A New Metaheuristic Algorithm % for Global Optimization (LO)% This work is published in Journal of "Applied …...
C#WPF动态资源和静态资源应用实例
本文实例演示C#WPF动态资源和静态资源应用 一、资源概述 静态资源(StaticResource)指的是在程序载入内存时对资源的一次性使用,之后就不再访问这个资源了。 动态资源(DynamicResource)指的是在程序运行过程中然会去访问资源。 WPF中,每个界面元素都含有一个名为Resources…...

游戏逆向中的 NoClip 手段和安全应对方式
文章目录 墙壁边界寻找碰撞 NoClip 是一种典型的黑客行为,允许你穿过墙壁,所以 NoClip 又可以认为是避免碰撞体积的行为 墙壁边界 游戏中设置了碰撞体作为墙壁边界,是 玩家对象 和墙壁发生了碰撞,而不是 相机 玩家对象有他的 X…...

nodejs+vue流浪猫狗救助领养elementui
第三章 系统分析 10 3.1需求分析 10 3.2可行性分析 10 3.2.1技术可行性:技术背景 10 3.2.2经济可行性 11 3.2.3操作可行性: 11 3.3性能分析 11 3.4系统操作流程 12 3.4.1管理员登录流程 12 3.4.2信息添加流程 12 3.4.3信息删除流程 13 第四章 系统设计与…...
Css Flex 弹性布局中的换行与溢出处理方法
Css Flex 弹性布局中的换行与溢出处理方法 CSS弹性布局(Flex)是CSS3中的一种新的布局方式,它能够帮助我们更加灵活地布局元素。在Flex弹性布局中,元素的布局仅依赖于父容器的设置,而不再需要复杂的相对或绝对定位。本…...

linux系统与应用
Windows中的硬盘和盘符的关系; 硬盘通常为一块到两块;数量与盘符没有直接关系;一块硬盘可以分为多个盘符,如c,d,e,f,g等;当然理论上也可以一块硬盘只有一个盘符;学习linux时,最好使用固态硬盘&a…...
MySQL的结构化语言 DDL DML DQL DCL
一、SQL结构化语言介绍 数据查询语言DQL:其语句称为“数据检索语言”,用以从库中获取数据,确定数据怎样在应用程序给出,保留select是dql(也是所有sql)用的最多的动词 数据操作语言DML:其语句包括动词insert…...
P5488 差分与前缀和
传送门:洛谷 前题提要:包含了简单的生成函数思想以及多项式乘法,是一道不可多得的多项式好题.故记录一下. 题意:给定一个长为 n 的序列 a,求出其 k 阶差分或前缀和。结果的每一项都需要对 1004535809取模。 对于差分和前缀和我们分开来讨论. 先讨论前缀和部分: …...
uboot启动流程-uboot内存分配
一. uboot启动流程 _main 函数中会调用 board_init_f 函数,本文继续简单分析一下 board_init_f 函数。 具体分析 board_init_f函数的第二部分:内存分配代码。 本文继上一篇文章的学习,地址如下: uboot启动流程-涉及board_init…...

LeetCode 面试题 08.02. 迷路的机器人
文章目录 一、题目二、C# 题解 一、题目 设想有个机器人坐在一个网格的左上角,网格 r 行 c 列。机器人只能向下或向右移动,但不能走到一些被禁止的网格(有障碍物)。设计一种算法,寻找机器人从左上角移动到右下角的路径…...

画CMB天图使用Planck配色方案
使用Planck的配色方案: 全天图: 或者方形图: 使用下面设置即可: import pspy, pixell from pspy.so_config import DEFAULT_DATA_DIR pixell.colorize.mpl_setdefault("planck")此方法不会改变matplotlib默认配色方案…...

成都瀚网科技有限公司:抖店精选联盟怎么用?
抖音精选联盟是抖音电商平台提供的一项服务,旨在为商家提供更多的推广机会和销售渠道。然而,很多人对于如何使用抖店精选联盟以及如何开通这项服务不太了解。本文将为您详细介绍抖店精选联盟的使用和激活流程。 第一节:如何使用抖店精选联盟 …...
第二章:最新版零基础学习 PYTHON 教程(第五节 - Python 输入/输出–如何在Python中打印而不换行?)
一般来说,从 C/C++ 切换到 Python 的人想知道如何在 python 中打印两个或多个变量或语句而不进入新行。由于Python print() 函数默认以换行符结尾。Python 有一个预定义的格式,如果你使用 print(a_variable) 那么它会自动转到下一行。 例子: # 输入:[csdn, csdnforcsdn] …...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...

从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...