Java高效解决依赖冲突

发生依赖冲突主要表现为系统启动或运行中会发生异常,99%表现为三种NoClassDefFoundError、ClassNotFoundException、NoSuchMethodError。下面逐一讲解一下定位技巧。

1  NoClassDefFoundError、ClassNotFoundException排查定位步骤

STEP1、发生NoClassDefFoundError首先要看完整异常栈,确认是否是静态代码块发生异常,静态代码块发生异常堆栈与jar包冲突有很明显的区别,出现"Could not initialize"、"Caused by: ..."关键字一般是静态代码块发生异常导致类加载失败:

java.lang.NoClassDefFoundError: Could not initialize class testing.User     at testing.Test.main(Test.java:23)Caused by: java.lang.RuntimeException: UserId Not found    at testing.User.getUserId(Test.java:41)    at testing.User.<clinit>(Test.java:35)    ... 1 more

因为静态代码块发生异常导致NoClassDefFoundError,修改静态代码块避免抛出异常即可。如果不是静态代码块发生异常导致的问题,继续下一步。

STEP2、如果不是静态代码块发生异常导致加载失败,异常message关键字中会明确显示缺失的类名称,例如:

java.lang.NoClassDefFoundError: org/apache/commons/lang/CharUtils    at testing.Test.main(Test.java:19)

STEP3、在IDEA中(快捷键Ctrl+N)查找异常栈中提示缺失的类在哪些版本的jar包中有,如上例中的org.apache.commons.lang.CharUtils

图片

STEP4、查看应用部署机器上应用lib包目录下(一般是/home/admin/union-uc/target/${projectName}/lib或union-pub/target/${projectName}.war/WEB-INF/lib)是否存在上一步骤中查出对应版本的jar包,以上情况一般是因为此时应用依赖的是低版本jar包,而jar包中又没有冲突的类,绝大部分情况下NoClassDefFoundError、ClassNotFoundException定位确认都是因为maven依赖仲裁最终采纳的jar包版本与运行时需要的不一致导致。

2  NoSuchMethodError排查到位步骤

STEP1、发生NoSuchMethodError,异常堆栈日志核心片段(异常栈中处于栈底的片段,见过很多同学发生异常乱翻一通,那样毫无意义,要有目的的翻关键地方,不要乱翻)会明确显示具体是哪个类,缺失了哪个方法,异常堆栈核心片段示例如下:​​​​​​

Caused by: java.lang.NoSuchMethodError: org.springframework.beans.factory.support.DefaultListableBeanFactory.getDependencyComparator()Ljava/util/Comparator;    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次。此时庞大的依赖导致部署效率,影响评估回归都会很难,此时就不应该从局部解决冲突这种视角去看,应该考虑优化应用架构,进行依赖治理,尽量避免冲突。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JavaOCR是一个开源的OCR(光学字符识别)Java库,可以在Java平台上进行图像识别和文字提取。要在Maven项目中使用JavaOCR,您需要将以下依赖项添加到您的pom.xml文件中: ``` <dependency> <groupId>net.sourceforge.javaocr</groupId> <artifactId>javaocr-core</artifactId> <version>3.0.0</version> </dependency> ``` 这将使您能够在您的Maven项目中使用JavaOCR库。您还可以查看JavaOCR的GitHub页面,了解更多关于JavaOCR的信息和用法。 ### 回答2: JavaOCR提供了Maven作为其项目管理工具。Maven是一个非常流行的Java项目管理和构建工具,它可以帮助开发者管理项目的依赖关系、构建和部署等方面的工作。 使用Maven作为JavaOCR的项目管理工具,可以大大简化项目的构建和管理过程。首先,开发者只需要在项目的pom.xml文件中定义依赖关系,Maven就会自动下载并管理这些依赖项。这样一来,开发者就不需要手动管理项目的依赖项,而可以专注于编写代码。 另外,Maven还提供了一套标准化的项目结构和构建生命周期。通过定义各个阶段的插件和任务,开发者可以在不同的构建阶段执行特定的操作,如编译代码、运行测试、生成文档等。这样可以确保项目的构建过程是一致、可重复的。 此外,Maven还提供了一些其他的功能和特性。例如,Maven可以自动下载依赖项的源代码和文档,方便开发者进行源码阅读和文档查阅。同时,Maven还支持多模块项目的构建,可以将一个大型项目分解为多个模块来管理。 总之,JavaOCR选择使用Maven作为其项目管理工具,可以帮助开发者更加高效地构建和管理项目,简化了依赖管理、构建过程和部署等方面的工作,提高了开发效率。同时,Maven还提供了一些其他的功能和特性,进一步增强了项目管理的灵活性和可扩展性。 ### 回答3: JavaOCR是一个用于图像文字识别的开源库,使用Java语言编写。它可以将图像中的文字自动识别并转换为可编辑的文本。 Maven是一个Java项目管理工具,可以帮助项管理项目的构建、依赖关系、测试和部署。使用Maven,我们可以更方便地管理JavaOCR的依赖和构建。 在使用JavaOCR时,首先我们需要在Maven项目的pom.xml文件中添加JavaOCR的依赖。通过指定依赖的坐标和版本号,Maven会自动下载并引入JavaOCR相关的jar包。这使得我们不需要手动下载和复制jar包到项目中,大大简化了项目的配置和管理过程。 除了依赖管理外,Maven还提供了一系列的命令和生命周期,用于构建、测试和部署项目。我们可以使用Maven的命令行工具或集成开发环境(IDE)来执行这些命令,如编译Java源码、运行单元测试和打包生成可执行的jar文件。 通过使用Maven,我们可以更轻松地管理JavaOCR的项目,快速搭建和部署开发环境。同时,Maven还能自动解决依赖冲突和版本管理,确保项目的稳定性和一致性。 总之,JavaOCR的Maven是为了更方便地管理JavaOCR项目的依赖和构建过程而引入的。它简化了项目的配置和管理,提高了开发效率,并保证了项目的稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值