一文彻底搞懂解决NoClassDefFoundError/NoSuchMethodError错误

一. 先看看什么是Maven的传递性依赖

Maven的传递性依赖

使用 Maven 工具,带给开发者最直观的好处就是:

  • 不用再去各个网站下载各种不同的jar包了,也不用考虑它们之间的依赖关系;
  • 只需把项目依赖的jar包信息配置在pom文件中,它就会帮我们提供好一个jar包和该jar包所依赖的其它jar包。
举例解释

比如,在项目中引入 easyexcel 后:

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>easyexcel</artifactId>

<version>2.1.6</version>

</dependency>

我们使用 Maven Helper 插件,查询依赖树:
在这里插入图片描述

可以看到:

  • easyexcel 依赖 cglib,cglib 又依赖 asm;
  • 因此 cglib 和 asm 这两个包也会被引进来。

通过查看 External Libraries 列表可以得到验证:
在这里插入图片描述

|二. Maven的最短路径原则和最先声明原则

思考一个问题,由于 Maven 的依赖性传递特性,如果有多个jar包同时都依赖一个jar包,且版本不一样的时候,如下图:
在这里插入图片描述

这时会遵循两个规则,分别是最短路径原则最先声明原则

最短路径原则

如上图:

  • jarA -> jarC(1.1)
  • jarB -> jarD -> jarC(1.2)

Maven 项目最终会使用 1.1 的 C 。

最先声明原则

那么当最短路径一样的时候该怎么办?
这时就需要最先声明原则,且最先声明原则就是在最短路径相同的时候生效,如下图所示:
在这里插入图片描述

由上图可以看出,依赖路径如下:

  • jarA -> jarC(1.2)
  • jarB -> jarC(1.1)

现在最短路径是一样的,则就需要看谁先声明的。
在工程的pom.xml里,可以看到是先引入的 jarA,那么根据规则,则jarC(1.2) 生效,Maven 会使用jarC(1.2) 来进行编译和打包。

三. jar包冲突:NoClassDefFoundError

软件开发通常都是会有不断的版本迭代,如果:

  • 在一个项目中同时会有多个jar包都依赖同一个jar包;
  • 且都依赖的这个jar进行了版本升级,

则此时工程就有可能会产生jar包冲突,下面将通过图解举例介绍。

1. jarC版本升级丢弃了G.class

假如jarC 版本升级,由1.1 版本 升级到 1.2后,G.class由于被修改而不存在了:

在这里插入图片描述

2. 编译期间为什么不报错

我们知道,Maven编译时会把业务代码编译成class文件。
比如下面这种场景,业务代码DemoController.java类引入了jarA中的A1.class,maven 编译时会将业务代码DemoController.java类编译为DemoController.class

在这里插入图片描述

备注:

  1. DemoController.class没有直接引用jarC中的G.class类,引入的A1.class在jarA中是存在的;
  2. 编译的目的只是把业务源代码编译成.class文件;
  3. 所以在项目在编译的时候不会报错。
3. 为什么运行期间报错:NoClassDefFoundError

项目部署成功后,当接收到前端发起的一次请求,需要调用selectList接口查询数据时,会因为在项目依赖的jar包里找不到G.class类而报错,一个详细的流程如下图所示:
在这里插入图片描述

备注:

  1. 由于要调用jarA中A1.classhandleWrite方法;
  2. 但是jarC 1.2版本已经没有G.class类;
  3. 所以在执行的时候会报错:NoClassDefFoundError

四. jar包冲突:NoSuchMethodError

1. jarC版本升级丢弃了G.class的method1

问题来了,jarC 版本升级,由1.1 版本 升级到 1.2后,G.class类的begin方法由于逻辑优化而不存在了,改为begin1

在这里插入图片描述

2. 编译期间为什么不报错

在这里插入图片描述

备注:

  1. DemoController.class没有直接引用jarC中的G.class,而引入的A1.class在jarA中是存在的;
  2. 编译的目的只是把业务源代码编译成class文件;
  3. 所以在项目在编译的时候不会报错。
3. 运行期间为什么会报错:NoSuchMethodError

在这里插入图片描述

备注:

  1. 由于要调用jarA中A1.classhandleWrite方法;
  2. 在执行jarA包里的A1.classhandleWrite方法时需要调用jarC包里的begin方法;
  3. 但是jarC 1.2版本里,G.classbegin方法已经不存在了;
  4. 因此在执行的时候会报错:NoSuchMethodError

五. 总结

  1. jar包冲突是java开发中经常会遇见且有的时候要耗时很久才能解决的问题;
  2. Maven 项目中jar包冲突大部分都是这两种场景,只有在弄清楚jar包冲突产生的原因后,我们才能够在遇见问题的时候行之有效的去排查、解决;
  3. 可以借助相关工具去帮助排查jar冲突原因(后边会以实例介绍解决思路):
    • 如 idea自带的Show Dependencies;
    • idea安装插件 Maven Helper,通过查看 Dependency Analyzer;
    • 通过命令行执行 mvn dependency:tree>temp/tree.txt,可以生成具有jar包依赖关系的文件。> 可以访问 这里 查看更多关于大数据平台建设的原创文章。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值