如何解决版本不兼容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] …...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
