java依赖冲突高效解决之道(代码片段)

阿里技术 阿里技术     2022-11-28     328

关键词:


   at testing.Test.main(Test.java:23)   at testing.User.getUserId(Test.java:41)   at testing.User.<clinit>(Test.java:35)   ... 1 more


at testing.Test.main(Test.java:19)

at org.springframework.context.annotation.AnnotationConfigUtils.registerAnnotationConfigProcessors(AnnotationConfigUtils.java:190) at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.registerComponents(ComponentScanBeanDefinitionParser.java:150) at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(ComponentScanBeanDefinitionParser.java:86) at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:73)

首先需确认JVM中当前加载的缺失方法类,如上"org.springframework.beans.factory.support.DefaultListableBeanFactory"类到底来自哪个jar包,目前最高效的办法:

外部环境容器下,或者某些容器版本过低不支持Arthas在线诊断的情况下,可以通过在JVM启动参数中增加" -XX:+TraceClassLoading",然后重新启动系统,在系统工程日志中即可看到JVM加载类的信息。从中即可找到JVM是从哪个jar包中加载的。

STEP2、在IDEA中(快捷键Ctrl+N)查找异常栈中提示缺失的类在哪些版本的jar包中有,如下图所示:


然后依次查看各版本jar包中冲突类的源码,工程中部分jar打包时附带了源码包可直接看到源码,不带源码的需要用IDEA插件(推荐jad)反编译一下。然后依次搜寻各个jar包中的冲突类,搜寻第一步是点击上图中某个版本类,在IDEA中查找类级次关系(快捷键Ctrl+H),如下图所示:


然后在冲突类及所有冲突类的父类源码中找到NoSuchMethodError异常信息中描述缺失的方法,以上例子中就是"getDependencyComparator()Ljava/util/Comparator"。

上例中通过搜寻可以发现spring-beans-3.2.1.RELEASE.jar,spring-2.5.6.SEC03.jar两个版本DefaultListableBeanFactory类及父类中没有"getDependencyComparator()Ljava/util/Comparator"方法,spring-beans-4.2.4.RELEASE.jar,spring-beans-4.3.5.RELEASE.jar两个版本DefaultListableBeanFactory类中有缺失的"getDependencyComparator()Ljava/util/Comparator"方法。

STEP3、查看应用部署机器上应用lib包目录下(一般是/home/admin/union-uc/target/$projectName/lib或union-pub/target/$projectName.war/WEB-INF/lib)下,找到相关jar包的版本,如上例中:


致此定位问题根本原因是应用启动时加载"org.springframework.beans.factory.support.DefaultListableBeanFactory"类未加载到运行时预期所需的spring-beans-4.3.5.RELEASE.jar版本,而是加载了spring-2.5.6.SEC03.jar导致。

按照以上流程步骤,基本99%的依赖冲突都可以定位到根本原因。定位到原因后如何解决冲突呢?事实上有些时候解决冲突远没有内网上很多帖子描述的"mvn dependency:tree"一下,排排jar那么简单。具体细节请继续看下一章节。

四  通过maven调整依赖jar解决依赖冲突


1  升降级jar包解决依赖冲突


上一章节中的第一个例子中,最简单的情况,如果发生冲突的jar包高版本是完全兼容低版本功能的情况下,只需在pom中简单升级jar包版本即可。

但如果冲突 jar包高版本不兼容低版本,且应用依赖不是很复杂的情况下,可以分析升级冲突jar包后会对哪些业务有影响,具体做法推荐通过IDEA Maven Helper 插件查找冲突jar包有哪些业务依赖(此处不推荐"mvn dependency:tree",目前本人见过的大部分Maven工程都有多个Module,比如*-dal,*-Service,*-Controller,这类工程结构如果module未单独打包上传Maven仓库,"mvn dependency:tree"是不能完整分析依赖关系的),记录下来。如下图所示:


然后升级冲突包,通过回归测试受到影响的二方库对应的业务点。

如果应用依赖非常复杂(例如冲突包有几十个二方库依赖,或者依赖冲突包的二方库是个基础包,业务系统中无法清晰枚举出使用受影响二方库的业务点),这种情况下,如果要通过升级jar包解决依赖冲突,必须完整回归整个应用功能。笔者有几次因为回归不全面引发故障的惨痛经历,希望大家不要重蹈覆辙。通过这几次事例,笔者深刻理解到我们这个时代最伟大的计算机科学家Dijkstra大神“简单是可靠的先决条件”这句至理名言,深深的体会到如果一个系统复杂到你完全无法理清楚他错综复杂的依赖关系的时候,那说明你该重构你的系统了,否则系统维护将会逐步变成噩梦。

当然不是所有情况都可以通过升降级jar解决冲突,举个例子:



如上图假设应用系统同时依赖A.jar,B.jar,而A.jar,B.jar都依赖protobuf-java,系统运行时都会分别用到A.jar,B.jar中protobuf部分的功能,而且A.jar,B.jar依赖的protobuf版本无法通过升高降低版本调整到一致。由于protobuf-java3.0版本序列化协议,类内容各方面都不兼容protobuf-java2.0版本。这种情况无论如何调整依赖都无法解决冲突的问题,要解决这种冲突,请继续往下看,第五第六章内容。

2  排除jar包解决依赖冲突


上一章节中第二个例子,主要原因是容器启动时加载到的类不是预期spring-beans-4.3.5.RELEASE.jar中的类,而是spring-2.5.6.SEC03.jar包中的类,如果spring-2.5.6.SEC03.jar排除对业务无影响,可以通过排除spring-2.5.6.SEC03.jar来解决冲突。与上一节例子类似,可以通过IDEA Maven Helper 插件确定spring-2.5.6.SEC03.jar是由哪个jar间接依赖进来的,判断业务的影响范围,此处不在赘述。与上一节一样,类似的情况不一定都可以用排除jar解决。

五  通过pandora自定义插件解决依赖冲突


第四章中有讲到,如果一个应用中要同时运行两个不兼容版本的jar包,是无法通过Maven调整依赖关系解决的。第二章讲解依赖冲突原理时有提到,Pandora通过类隔离机制实现了集团各个中间件之间的隔离,Pandroa同时也支持业务方按规范创建一个可以运行在Pandora容器中的插件,容器帮业务方实现加载隔离。

联盟一淘团队就将类似IC、卡券这种核武器级存在的二方包根据自己业务的需要进行裁剪包装后,制作成Pandora插件来避免依赖冲突,取得了很好的效果。

用Pandora插件确实能在不对应用做很大调整,不影响性能的情况下完美解决依赖冲突问题。

但也有一些问题就不太适合用局部方法解决了,比如:

当维护的应用依赖过于复杂,每个应用依赖外部三四十个二方库时。这种重量级应用就会严重影响生产效率。


如上图所示,早期本人负责联盟用户平台时,就遇到两个巨无霸应用,adv(6w+代码)、pub(12w+代码)。

一方面因为依赖多,基本每周都会遇到集团各种升级,安全问题,各种小修小补,不断的上线。一方面业务发布需求也较多。

导致需要频繁发布,比如有一年个人就发布了566次。此时庞大的依赖导致部署效率,影响评估回归都会很难,此时就不应该从局部解决冲突这种视角去看,应该考虑优化应用架构,进行依赖治理,尽量避免冲突。

六  通过依赖架构治理解决依赖冲突


1  复杂依赖标准化、简化治理


首先,依赖本身就是一种复杂的业务。大部分依赖背后都有较深的业务领域知识 或者 技术领域知识。

比如我们查询搜索。

业务领域知识方面,光销量就有交易成交笔数,成交件数,搜索销量【有些订单不计入搜索销量】等。

技术领域知识方面,主搜索,联盟广告搜索引擎有时是配合使用的,比如商家未入驻广告前给商家展示货品信息就需要查主搜索,而入驻后投放下行时则需要用广告引擎。不同引擎的调用方法,结果都不一样。

如下图所示,如果我们每个业务应用都各自实现,那么各应用开发同学就要消化大量搜索客户端相关的业务、技术领域知识。成本是很高的。


面对这种情况,如果我们将这类复杂的依赖,由专人owner进行统一包装标准化【专人干专事】,会大大提升组织协同效率。如下图所示。


我们通过对主搜索,联盟引擎的统一封装。对检索条件,返回结果的标准化封装。大大降低了同学们的接入成本,以往要熟悉一个引擎的接入大概要2天,用标准化封装后的wrapper,在专人,规范文档的指导下仅0.5天就可以,大大提升效率。

2  重量级依赖代理服务化


第五节中有讲到,应用依赖的jar包过多会导致应用启动很慢,因此如果一个依赖引入jar包超过30个以上时,务必要警惕,这种依赖引入几个,就会逐步导致你工作效率大大下降。比如IC,TP,优惠中心的二方包就是典型的例子。

目前我们针对这类依赖,是直接封装一个标准代理服务,避免应用被这种巨无霸二方包拖慢。


经过以上综合治理手段,取得了很好的效果。目前联盟很少再需要大家去解决冲突问题。


参考链接:
INode讲解:http://www.cnblogs.com/itech/archive/2012/05/15/2502284.html



数据分析系统之数据管理与数据仓库
点击阅读原文查看详情!

java类隔离解决依赖冲突nosuchmethoderror(代码片段)

Java开发过程中经常遇到NoSuchMethodError异常:org.springframework.web.util.NestedServletException:Handlerdispatchfailed;nestedexceptionisjava.lang.NoSuchMethodError:com.xxx.xxxMethod()atorg.springframework. 查看详情

gradledependencies解决项目依赖冲突(代码片段)

1.查看版本号$gradle-v编译打包检查依赖并编译打包gradlebuild编译跳过测试gradlebuild-xtest编译打包tgz包,跳过测试类gradlereleaseTarGz-xsignArchives-xtest这里注意的是gradlebuild命令把debug、release环境的包都打出来,如果正式发布只需... 查看详情

gradledependencies解决项目依赖冲突(代码片段)

1.查看版本号$gradle-v编译打包检查依赖并编译打包gradlebuild编译跳过测试gradlebuild-xtest编译打包tgz包,跳过测试类gradlereleaseTarGz-xsignArchives-xtest这里注意的是gradlebuild命令把debug、release环境的包都打出来,如果正式发布只需... 查看详情

使用mavenhelper解决maven依赖冲突(代码片段)

1、何为依赖冲突Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的。Maven的依赖机制会导致Jar包的冲突。举个例子,现在你的项目中,使用了两个Jar包,分别是A和B。现在A需要依赖另一个Jar包C,B... 查看详情

maven依赖冲突以及解决方法(代码片段)

什么是依赖冲突依赖冲突是指项目依赖的某一个jar包,有多个不同的版本,因而造成类包版本冲突依赖冲突的原因依赖冲突很经常是类包之间的间接依赖引起的。每个显式声明的类包都会依赖于一些其它的隐式类包,... 查看详情

intellijidea2022.1正式发布!新增依赖分析器,轻松解决依赖冲突问题。。(代码片段)

...该版本的重点功能是:引入了 DependencyAnalyzer 以促进依赖关系管理和冲突解决,一个更新的 NewProject 向导来优化新项目的启动过程,以及 Notifications 通知工具窗口࿰ 查看详情

intellijidea2022.1正式发布!新增依赖分析器,轻松解决依赖冲突问题。。(代码片段)

...该版本的重点功能是:引入了 DependencyAnalyzer 以促进依赖关系管理和冲突解决,一个更新的 NewProject 向导来优化新项目的启动过程,以及 Notifications 通知工具窗口࿰ 查看详情

怎么排除maven依赖(代码片段)

文章目录怎么排除maven依赖1.下载mavenhelper插件2.查看依赖冲突3.解决冲突怎么排除maven依赖当我们项目中引入其他模块时,就会产生依赖冲突,发生冲突会产生类似的报错:Anattemptwasmadetocallamethodthatdoesnotexist.Theattemptwasm... 查看详情

androidgradle插件android依赖管理⑤(gradle依赖优化|命令行查看依赖模块|依赖冲突问题|依赖传递冲突|分库冲突|依赖分组不同导致冲突)(代码片段)

文章目录一、Gradle依赖优化二、命令行查看依赖模块1、使用最高版本依赖选择2、排除重复依赖3、排除重复依赖+最高版本依赖选择三、依赖传递冲突解决方案1、依赖传递冲突2、分库冲突3、赖分组不同导致冲突4、解决思路一... 查看详情

依赖冲突了如何解决?(代码片段)

...名其妙的异常信息,这种情况一般很大可能就是jar包依赖冲突的问题引起的了。至于为什么会发生jar包依赖冲突?这种问题大致 查看详情

android解决之依赖包中添加的com.android.support版本不同导致冲突的问题(代码片段)

依赖包:implementation'com.android.support:appcompat-v7:25.0.1'implementation'com.android.support.constraint:constraint-layout:2.0.1'添加第二个依赖包后和第一个依赖包就冲突了解决:implementation' 查看详情

maven:maven_02:依赖管理与冲突解决及项目继承聚合(代码片段)

...的文章有点feel,那就点个赞再走哦。文章目录Maven的依赖范围maven的依赖范围包括:具体细节对比:代码展示:Maven的常用设置全局变量:引用全局变量:Maven系统采用的变量指定资源位置问题: 查看详情

程序员需要了解依赖冲突的原因以及解决方案(代码片段)

0x00.前言依赖冲突是日常开发中经常碰到的过程,如果运气好,并不会有什么问题。偏偏小黑哥有点背,碰到好几次生产问题,排查一整晚,最后发现却是依赖冲突的引起的问题。没碰到过这个问题同学可能没什么感觉,小黑哥... 查看详情

androidnestedscrolling解决滑动冲突问题-相关接口(代码片段)

...ayout内嵌RecyclerView或者Webview),滑动冲突的解决就需要依赖于Android为我们提供的NestedScrolling接口。NestedScrolling接口分为两个部分:NestedScr 查看详情

java双亲委派问题&解决实战(代码片段)

接上一篇:Java双亲委派问题&解决实战(一),讨论依赖中的类加载问题设想如下场景demo1的1.0;2.0;3.0三个版本分别被其它模块依赖;如果业务bundle-main要正常运行依赖结果,这显然要冲突项目工程如下:... 查看详情

全总结(代码片段)

...#xff0c;因为我们公司的项目都是使用maven管理项目的第三方依赖,一直都只会一种解决方法就是锁定版本,今天好好总结一下,记录一下。1、冲突原因:jar包冲突主要是由于依赖传递导致的,不同的jar包依赖同... 查看详情

vscode中prettier插件&依赖安装及冲突解决(代码片段)

...式化工具1.4配置自动格式化1.5与ESLint冲突解决二、Prettier依赖安装2.1安装依赖2.2配置2.3配置指令2.4其他配置和冲突解决一、Prettier插件安装1.1安装Prettier插件通过VSCode安装插件,如图:1.2添加Prettier配置文件在项目根目录&#x... 查看详情

flutter开发中遇到的问题(代码片段)

Flutter处理依赖冲突当Flutter发生版本依赖冲突会有类似以下提示:flutter_localizationsanyfromsdkwhichdependsonintl0.15.8,intl0.15.8isrequired.So,becauseenv_pro_supervisedependsonintl^0.16.0,versionsolvingfailed.我们只要在pubspec.yaml中将依赖改为相同版本。如果... 查看详情