当前位置: 首页 > 后端技术 > Java

版本不兼容Jar包冲突怎么办?

时间:2023-04-02 01:44:29 Java

1。“老婆”和“妈妈”同时落水,谁先被救?俗话说:五分钟编码,两小时解决冲突。作为Java开发,当你第一次看到ClassNotFoundException、NoSuchMethodException等异常时,第一反应就是安排包。经过套路和非常规操作后,往往会发现同一个Jar包引入了多个不同的版本。这时候一般排除掉低版本,保留高版本就可以了。这是因为一般的Jar包是向下兼容的。的。但是,如果出现版本不兼容,如果中间件依赖和业务本身发生这种不兼容,就会陷入“老婆和妈妈同时落水,先救谁”的困境,更是难上加难。如下图,Project代表我们的项目,DependencyA代表我们的业务依赖,DependencyB代表中间件依赖。如果业务依赖和中间件依赖都依赖同一个Jar包C,但是版本不一样,分别是0.1版本和0.2版本,最不幸的是这两个版本还是有冲突。一些老功能只存在于0.1的低版本,一些新功能只存在于0.2的高版本。在水里,不可能先救人。”(图片摘自:SOFAArk官网)俗话说:没遇到过Jar包冲突的开发者,一定是假Java开发者;没有解决Jar包冲突的开发者不是合格的Java开发者。在最近的一个项目中,我们需要使用Guava的高版本Jar包,但是发现中间件依赖了低版本的Jar包,与高版本不兼容。面对这种困境,我们必须做“妻子”和“妈妈”。”他们都救了,所以我们开始寻找解决方案。2.IncompatibleDependencyConflictSolution“老婆”和“妈妈”都需要救,怎么救?首先我们想到的是能不能把需要用到的高版本Guava的代码直接复制到我们的项目中,但是这样做会带来几个问题:代码,牵一发而动全身,工作量会比想象中大很多;复制的代码只能自己手动维护,构建代码或添加功能后,如果要升级,只能全部搞定重来一遍。所以,只能想别的办法了,这也只能是不得已的办法了。那么,我们就在想,加载到JVM虚拟机中的一个Java类和另一个Class是不一样的。一是两个完整路径不同。它们是两个不相关的不同类,但是它们被不同的类加载器加载,它们在JVM虚拟机中仍然被认为是两个不同的类。因此,我们从类加载器中寻找解决方案。在阿里巴巴内部,有一个潘多拉组件。就像它的名字一样,它就像一个神奇的盒子。它会将所有中间件依赖项安装到Pandora(内部称为Sar包)中。代码直接呈现了“老婆和妈妈同时落水,先救谁”的困境。同样,它也可以在应用合并部署等类似场景中发挥作用。不过Pandora只是阿里内部使用,并未开源。在蚂蚁金服里面,也有这样一个组件,而且是开源的,叫做SOFAArk(官网,有兴趣的可以去官网了解SOFAArk的原理和使用),感觉找到了Mr.Right,于是我们开始研究SOFAArk如何使用。和Pandora一样,SOFAArk使用不同的ClassLoader来加载不同版本的三方依赖,从而实现类隔离,彻底解决包冲突问题。这就需要我们将相关的依赖打包到ArkPlugin中(参见SOFAArk官方文档)。对于公司来说,这样的解决方案带来的好处是比较大的。打包成ArkPlugin后,全公司共享,业务方受益。但是,对于我们的项目来说,采用这样的方案无疑是过于沉重了。于是联系了我们的中间件同学,询问有没有计划引入类似的隔离组件来解决中间件与业务代码的依赖冲突。回答是公司目前的包冲突不是强痛点,也没有引入的计划。因此,只能暂时搁置SOFAArk,继续寻找新的解决方案。接下来我们在想,由于Pandora/SOFAArk使用类加载来隔离同路径下的类,如果我们将两个冲突版本库的groupId改成不同的,那么即使是同名类的全路径也会不同.JVM中肯定有不同的Class。如果把Pandora/SOFAArk的隔离方式叫做逻辑隔离,那么就相当于物理隔离。要做到这一点,借助IDE的重构功能或者全局替换功能,还是比较容易实现的。当我们准备撸起袖子开始干活的时候,不禁想到,这样的痛点应该早就有人遇到过,尤其是像Guava、Commons这样的基础类库,冲突在所难免,前辈们应该都有发现优雅的挠痒痒的手势。于是,我们搜索了相关文章,果然maven-shade-plugin就是优雅的挠痒痒的手势。这个Maven插件的原理是重映射类的包路径,达到隔离不兼容Jar包的目的。.3.maven-shade-plugin解决依赖冲突最后,如何配置使用maven-shade-plugin将Guava映射到我们自己定制的Jar包中,实现与中间件Guava的隔离。整个过程比较清晰,主要是创建Maven项目,引入依赖,配置我们要发布的仓库地址,引入编译打包插件和maven-shade-plugin插件,配置映射规则(partbetweentags),然后编译打包发布到Maven仓库。pom.xml的配置如下:4.0.0com.shaded.exampleguava-wrapper<版本>${guava.wrapper.version}guava-wrapperhttps://example.com/guava-wrapperUTF-827.1-jre1.81.8com.google.guavaguava27.1-jre<插件><插件>org.apache.maven.pluginsmaven-compiler-plugin3.3${maven.compiler.source${maven.compiler.target}org.apache.maven.pluginsmaven-jar-plugin2.3.2default-jarjarorg.apache.maven.pluginsmaven-source-plugin2.4default-sourcesjar-no-forkorg.apache.maven.pluginsmaven-shade-plugin2.4.1falsepackageshadecom.google.guavacom.google.guava.wrapper<重定位><模式>com.google.commoncom.google.common.wrapper<重定位>com.google.thirdpartycom.google.wrapper.thirdparty项目引入这个新打包的guava-wrapper后,import选择从中导入我们需要的相关的这个包类可以如下:com.vivo.internetguava-wrapper27.1-jre4.结论以在同一个项目使用多个版本不兼容的Jar包,我们首先想到的是自己手动维护代码,但是工作量和维护成本都很高。于是我们想到了通过类加载器进行隔离(开源方案SOFAArk),但是我们需要将所有相关的依赖打包到ArkPlugin中,这样的方案无疑是有点太重了。最后对maven-shade-plugin插件进行更名打包,优雅的解决了项目中多版本Jar包不兼容的冲突问题。我们从问题出发,一步步探索解决问题的方法。虽然最终的maven-shade-plugin插件方案看似与手动自维护代码一致,看似回到了原点,但实际上最终的方案远比最初的方案优雅。更高,就像人生的道路,盘旋上升,曲线前进。如果遇到需要支持不兼容Jar包共存的场景,可以考虑使用maven-shade-plugin插件。这种方式比较轻量级,可以用于项目中存在个别不兼容Jar包冲突的场景。它简单有效。成本也很低。但如果Jar包冲突比较普遍,已经成为明显或常见的痛点,建议考虑文中提到的Pandora、SOFAArk等类加载器隔离方案。作者:vivo互联网服务器团队-张伟