别再因为Jar包冲突砸键盘了
highlight: a11y-dark theme: condensed-night-purple
Maven Jar冲突时我们在开发过程中最常见并且头大的问题,经历几次后,不要再“一顿操作猛如虎,定睛一看原地杵”。先了解一下Jar冲突的原理,再遇到的时候不再“一腔怒火砸键盘”了。
原理
依赖传递
当项目引入A依赖时,同时A依赖引入了B,B可能引入了C。最后,Maven会把所有相关的依赖都添加到项目中,好处是不用自己去添加所有依赖的Jar包了,但是可能会引起Jar冲突。
举个栗子,比如spring boot工程中,引入spring-boot-starter-web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
(不同级)最短路径优先原则
两个版本不同的同一个依赖,在不同的级别中,会优先使用路径最短的一个。
pom -> A -> B -> W(14.0.1)
pom -> A -> C -> D -> W(12.0.1)
最终会优先引入W(14.0.1)。
(同级)声明优先原则
后声明优先:两个版本不同的同一个依赖,若都在第一级,会优选使用后声明的依赖。
pom -> W(14.0.1)
pom -> W(12.0.1)
最终会优选引入W(12.0.1)。
先声明优先:不在第一级的话,会优选使用先声明的依赖。
pom -> A -> W(14.0.1)
pom -> B -> W(12.0.1)
最终会优选引入W(14.0.1)。
Jar包冲突原因
首先,在平时开发中可能会因Jar冲突遇到的异常有:
java.lang.ClassNotFoundException
java.lang.NoSuchMethodError
java.lang.NoClassDefFoundError
根据上面讲的原则,每一个原则都会导致Jar冲突。所谓的Jar冲突,就是项目最终引入的依赖,程序在某个方法使用了低于/高于内设版本的jar包,没有找到对应的类或方法,导致程序错误。
排查Jar冲突
IDEA Maven Diagrams
使用IDEA自带Maven插件,在pom.xml右键,选择Diagrams,点击show Dependencies,查看Maven依赖层级结构。
红色线表示冲突,可以看到Maven选择了哪个版本的依赖。
Maven Helper
安装Maven Helper插件,直接可以看到发生冲突的依赖。
右键可以直接排除冲突的依赖,会自动在pom.xml添加排除属性。
mvn dependency
本地开发可以借助IDEA来排查Jar包冲突,那在测试或生产环境的话,可以使用mvn命令来定位冲突。
#打印依赖树
mvn dependency:tree
#将依赖树打印到文件中
mvn dependency:tree > a.txt
#打印包含某些包的依赖树
mvn dependency:tree -Dincludes=guava
解决Jar包冲突
exclude 排除
在pom.xml通过exclude排除,如下:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
指定版本
在很多情况下,采用exclude排除的话,可能需要排除的位置很多,可以根据上面提到的原则,在pom.xml直接引用指定版本的依赖,这种方式的优先级最高。
其它小技巧
服务器查看Jar包版本
jar tf jar包名称