理解并解决Maven版本冲突

什么是maven版本冲突

从github上拉了一份代码本地运行报错,
Caused by: java.lang.ClassNotFoundException
理解并解决Maven版本冲突_第1张图片

提出问题

  1. 为什么类不存在,在编译阶段没有被发现,可以顺利通过编译?

这里解释一下编译和打包的含义,编译针对的是我们项目写的java源码,显然不包括依赖的jar,编译完毕之后java源码就变成了可以被执行class字节码,而打包的含义是,将字节码打包成一个可执行的jar包 ,我们依赖的jar包本身就是字节码,不在编译范围

  1. 如果java源码显式的使用了依赖jar包中的类,那么就要求依赖的jar包在编译阶段一定要生效。否则会报错,编译失败。
  2. 也有的jar包可能没有没直接被当前项目的java源码显示使用,但是运行时会被会使用,比如被项目的依赖的某个模块进行依赖并且需要调用,那么这种错误是无法在编译阶段发现的。到运行阶段才会发现,那么对于这种jar包需要保证运行阶段有效。

那么我们知道为什么ClassNotFoundException会在运行时才会被发现之后继续提出问题

  1. 我们依赖的jar包,都是能被正常打成jar包,也就是在打包时是可以正常通过编译的,必然也不会出现其使用的使用的某个类不存在的情况。那么为什么来到我们使用的时候就出现了类不存在的情况?

这里要说一个依赖传递的概念
依赖传递的意思是项目A依赖项目B,项目B依赖项目C,在使用项目A时,就会加载项目B,这样传递依赖就会把项目C,D,E等等加载进来。其中B是A的直接依赖,C是A的间接依赖
例如:A项目依赖B(版本为1.1),C项目也依赖B(版本为1.2),假如现在有一个项目同时依赖项目A和项目B,那么他就会导入两个版本的B,由于B的两个版本不同,这里就会导致冲突,这个时候就需要exclusions来解决冲突,不过maven也有一个机制会避免两个都加载进去,
下面先介绍maven的依赖调节原则:
1,第一原则:路径近者优先原则
A→B→C→X(1.1)
D→E→X(1.2)
使用X(1.2),因为其路径更近
2,第二原则:第一声明者优先原则
A→B→X(1.1)
C→D→X(1.2)
使用X(1.1),因为其先声明
maven会先根据第一原则判断,若路径相等,再根据第二原则判断


知道了依赖传递后举个例子,我们当前项目是A ,-> 表示依赖
A->B->C(1.1)
同时A->C(0.5)
那么根据路径近有效,我们整个项目所使用的就是0.5版本的C。而B需要使用C种的某个类在0.5种没有。那么就会发生编译本问题,运行时报错ClassNotFoundException**,这就是maven冲突**

如何解决maven冲突

  1. 首先明确缺失类或者方法在那个jar包中

例如我这个报错

Caused by: java.lang.ClassNotFoundException: com.alibaba.spring.context.OnceApplicationContextEventListener

根据包路径确认冲突jar包是spring-context-support

  1. 知道了是那个jar包冲突,实际上我们就可以人肉分析了,仔细分析pom文件,根据依赖传递的原则就可以慢慢找到,当然,项目依赖关系复杂的时候是十分困难的事情,因此需要借助工具安装用Dependency Analyzer插件分析
  2. 分析结果

比如我们的项目引入A的依赖C为1.1版本,引入的B会在内部依赖C的1.0版本,那么Dependency Analyzer插件会出现依赖冲突提示,会提示B引入的C的1.0版本和当前选用的C的1.1版本冲突因而被忽略(1.0 omitted for conflict with 1.1)。因此我们主要关注的就是omitted 这种情况
理解并解决Maven版本冲突_第2张图片

像我这个例子,dubbo依赖的spring-context-support1.0.11被替换冲突替换成了1.0.5
根据依赖层路径可以看出来,最下面这个是路径最短,那么起决定性作用的就是这个了,如果将其remove或者exclude掉,**那么根据,路径相同,先生命优先,**版本就会被替换成为1.0.11,我本地的问题到这里旧被解决了。

  1. 常见误区,并不是说,替换成高版本就完事大吉了。因为可能新版的jar包不兼容旧版本也会出现,类或者方法缺失的情况。同样当出现冲突omitted也不意味着一定有问题会报错,也有可能版本之间也是相互兼容的。因此在有些情况下解决依赖冲突可能不是一蹴而就的,往往需要尝试。

你可能感兴趣的:(maven,maven,java,spring)