首页 > 编程知识 正文

瑟提国服上线时间(陈萍萍上线)

时间:2023-05-06 21:06:31 阅读:77472 作者:3588

当APP应用程序尝试调用类`静态或实例'的指定方法时,会抛出类` java.lang.NoSuchMethodError '错误。 简而言之,它调用了在同一Class中有多个版本的实现且在运行时缺少方法的版本。 本文总结了NoSuchMethodError的常见原因及其解决方法,耐心阅读本文后,再也不用担心面试官问你什么是NoSuchMethodError了。

运行时抛出 NoSuchMethodError 的根本原因是什么?

在实际生产系统中,我们关注运行时抛出的` NoSuchMethodError '错误。 如果此错误较轻,则程序会异常终止,严重时,支付服务运行异常,实际支付完成,但也可能会发生意外的程序结果,如向用户返回支付失败。

运行时抛出“NoSuchMethodError”错误的根本原因是,APP应用程序直接或间接依赖于同一类的多个版本,而运行时没有方法的版本。 如下图所示。

因此,中心问题是,为什么同一个类有多个版本? 哪个版本的类将最终运行?

为什么同一个 Class 会出现多个版本?

中,Java Class出现多个版本的原因可以归纳为以下类别:

JDK版本不匹配。 在编译包环境中,通常使用较高版本的JDK进行开发打包,但在实际运行环境中,它是较低版本的JDK。 例如,本地项目环境的JDK版本为1.7,调用` Character.isAlphabetic () `方法以确定当前字符是否为字母。 联机环境的JDK版本为1.6,运行时抛出` NoSuchMethodError '错误。 快照版本不匹配。 在本地更新快照版本后,“mvn clean deploy”部署经常不运行,并且在联机环境中运行时保持对较早版本的快照包的引用。 Maven依赖于生命周期为provided。 本地依赖关系的常见组件的生命周期为“provided”,声明的版本仅用于本地编译包,在联机运行时通过其他依赖关系加载Jar包。 同一Jar包有多个版本。 在Maven依赖关系中,由于没有明确指定版本号,因此会间接发生版本依赖冲突,从而更容易部署低版本的Jar包。 同一Class将显示在不同的Jar包中。 代码复制场景中常见此问题,例如,基于开源版本定制了一些功能,并使用新的Maven坐标将其打包发布。 在这种情况下,Maven仲裁机制将被禁用(非常隐蔽,难以进行故障排除)。 由于JVM类加载器只对同一类加载一次,因此最终加载的类实现受Jar包依赖的路径、类声明的优先级或文件加载顺序等因素的影响,导致每台计算机加载的类实现不一致有两个重要因素影响

哪个版本的 Class 最终会被执行?

class是否最终运行。 如下图所示,Maven依赖于仲裁机制和JVM类的加载机制。

首先,Maven依赖仲裁机制来确定打包优先级,仲裁优先级按“从高到低”排序如下:

优先根据依存关系管理[从属管理]要素中指定的版本进行仲裁; 如果没有版本声明,则根据“短路径优先”原则(Maven2.0 )进行仲裁,并在从属树中选择路径最短的版本。 路径长度一致的,按照“第一声明优先”原则仲裁。 也就是说,选择POM中最初声明的版本。 合理使用Maven依赖仲裁机制可以方便地管理Jar软件包的版本。 不正确的使用会引起多个版本的Jar冲突。

然后,JVM类的加载机制决定将Class加载到JVM中的优先级。 如果多个Jar包中存在相同的类,则在ygdyt代理类的加载机制下,加载Jar包的类加载器级别越高,Jar包加载得越早,包含的Class首先执行,如上图所示

引导装载器(Bootstrap ClassLoader )具有最高的优先级,主要装载JVM运行时核心类,如(java.util )、(java.io ),这些类主要是) $ Java rt.jar ) )上的扩展类加载器(Extention ClassLoader )的优先级如下,主要加载JVM扩展类(如(swing组件)、(xml解析器),这些类主要是“APP应用程序类加载器”(Application ClassLoader )也称为系统类加载器,它再次加载由Classpath环境变量定义的路径中的Jar包和目录。 通常是我们自己

编写的代码或依赖的第三方 Jar 包都是由它来加载。

除了上述两种原因外,在同一个 ClassLoader 下,如果存在一个 Class 出现在不同的 Jar 包中,那么文件系统的文件加载顺序也可能会影响最终的加载结果。因此,应该尽量保证开发/测试/生产系统环境一致性。

如何解决 NoSuchMethodError 错误?

虽然抛出 `NoSuchMethodError` 错误的原因多种多样,但本质上是由于编译时类路径与运行时类路径不一致。因此,通用的定位思路可以归纳为以下 3 步:

定位异常 Class 的全限定类名与调用方,通常可以在应用日志抛出的异常堆栈中获取。如下图所示: Exception in thread "main" java.lang.NoSuchMethodError: com.xxx.AsyncAppender.append(Ljava/lang/String;)Ljava/lang/String; at com.xxx.ProvokeNoSuchMethodError.main(ProvokeNoSuchMethodError:7) at ……

2. 定位异常 Class 的来源,可以通过 Arthas 等在线诊断工具反编译,如 `jad com.xxx.AsyncAppender`,获取该类运行时的源码、ClassLoader、Jar 包位置等信息。

如果应用程序启动失败,或者无法进行在线诊断,可以考虑添加 JVM 启动参数 `-verbose:class` 或 `-XX:+TraceClassLoading`,在日志中将输出每个类的加载信息,比如来自哪个 Jar 包。

3. 根据 ClassLoader 和 Jar 包全路径名等信息,判断是类加载、Maven 仲裁或其他原因,并对应的加以解决。

如果是同一个 Jar 包的多版本问题,可以在 Maven `<dependencyManagement>` 标签中指定实际需要的版本,或者移除间接依赖中的低版本(提示: 执行 `mvn dependency:tree` 命令,可以查看 Maven 依赖拓扑关系)。

如果是同一个 Class 出现在不同的 Jar 包问题,若可以排除,就用 `<excludes>` 排除该依赖;如不能排除,则考虑升级或替换为其他 Jar 包,或者考虑使用 ClassLoader 隔离技术。

其他 Jar 包冲突问题

本文介绍的 Jar 包冲突解决方法,除了解决 `java.lang.NoSuchMethodError` 以外,对其他相似问题也具备一定的参考价值。

例如 `java.lang.ClassNotFoundException`,即加载不到指定类,通常是 Maven 仲裁选错了版本,如本地开发阶段调用了 1.2.0 版本,而打包时采用了 1.0.0 版本的 Jar 包。同理,`java.lang.NoClassDefFoundError` 和 `java.lang.LinkageError` 也可以基于上述思路进行排查。

此外,如果类和方法名都保持不变,但是内部实现有变化,在多版本冲突场景下,不会抛出异常,但程序行为跟预期不一致,此时,也可以基于上述思路进行排查诊断。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。