1.jar包冲突的背景?
我们在maven项目中会通过坐标来引入jar包,而maven本身有个依赖传递的特性,就是说我们使用坐标引入A,而A又依赖了B,这时候我们无需在pom.xml文件中显示的引入B,因为B会根据maven依赖传递的特性自动引入。
maven依赖传递的特性,例如:
A-依赖->B(1.0版本)
C-依赖->D-依赖->B(2.0版本)
这种场景下,maven会把A、C、D、B(1.0版本)、B(2.0版本)这些jar全部下载到仓库中,但是,由于B有2个版本,使用的话肯定是只能使用一种,这里就用到了maven另一个特性依赖调节。maven依赖调节的解决方案1,即:路径最近者优先,针对上面的场景B(1.0版本)路径为2,而B(2.0版本)的路径为3,所以maven会使用B(1.0版本)。但是还有一种场景就是:
A-->E-->B(1.0版本)
C-->D-->B(2.0版本)
这种路径相同的依赖,maven的解决方案2,即:第一声明者优先,也就是在pom文件中谁在前面就用谁,这里由于A在前,所以使用的是B(1.0版本)。
2.什么是jar包冲突?
由于maven依赖传递的特性,会把很多同一个jar的不同版本都下载下来,maven会根据依赖调节来选择出一个版本来使用,一般大部分情况下程序不会出现问题,我们是不需要关心的,但是,有一种特殊情况,还是之前的例子,如果在程序中我们使用到了B(2.0版本)中某些类或者方法,而这些方法和类在B(1.0版本)中不存在,但是由于maven的特性,它选择使用了B(1.0版本),这时候程序运行就会报错找不到这个类或者方法,这种情况就是我们需要关心的jar包冲突(都报错了,能不关心吗...)。
这种jar冲突错误,本质上是因为这些依赖是通过maven的依赖传递来引入的,而由于maven的依赖调节机制选择了错误版本的jar,而不是在POM中使用<dependency>直接引入的。当然如果是<dependency>直接引入的话,路径为1,maven肯定会优先选择,根本轮不上其他依赖来进行依赖调节,这种要是报错找不到类或者方法,改<version>版本就行了,最直接了。
3.如何解决jar包冲突?
上面了解了jar包冲突的根本原因,那怎么解决呢?
- 通过mvn dependency:tree 命令打印依赖关系,查看依赖冲突
- 通过查看diagrams图来查看冲突的jar包
- 通过插件查看冲突的包并解决冲突
这里重点介绍第三种:在idea中使用maven helper插件来查看冲突,这种是目前个人感觉最好用的方式。
首先安装maven helper:settings->plugins->marketplace搜索maven helper下载重启idea。
在pom.xml文件下方点击Dependency Analyzer显示如下图:
1:刷新该界面,当你在该界面排除依赖后,需要点击1来刷新界面。
2:按照名称搜索包(搜索的是4区域的包)
3:单选框,主要是选择4区域展示jar的方式:只显示有冲突的jar
显示所有jar
显示所有jar以树的方式
4:展示的是我们pom中的依赖
5:当前在4中选的jar,根据maven的传递依赖已下载下来的所有版本。白色为maven选择使用的版本,红色为冲突的maven没有选的版本。以图中为例,1.7.6版本为红色,在maven插件的6处查看1.7.6版本确实为灰色,表示maven没有使用(6中显示1.7.6版本与1.7.25版本冲突是因为我修改了依赖,排除了1.7.25,但是插件还没刷新)。
区域5这一块是重点查看的地方,这个树结构要从上往下,从内往外看,上图中依赖关系为:
log4jdbc:1.2 依赖 slf4j-api:1.6.0
curator-client:3.3.0 依赖 zookeeper:3.5.1-alpha 依赖 slf4j-api:1.7.5
curator-client:3.3.0 依赖 slf4j-api:1.7.6
检查出来冲突之后,只需要排除错误的依赖即可,可以鼠标右键点击要排除的依赖,选择Exclude即可,它会在对应的依赖下添加<exclusion>来排除所选依赖。Jump To Source表示跳转到pom文件该依赖的位置。(这里排除的话,我理解第一个先要排除的就是maven使用的版本,毕竟使用这个版本都报错了嘛,不行的话在排除其他的,试就对了。)
这里红框标注的地方,树的节点只有一个,没有子节点,这个表示这个版本的依赖是在pom文件中使用<dependency>来明文引用的,这个的路径依赖为1,所以说maven会直接选择这个使用,不要的话无法Exclude,只能去pom中注掉。
至此,我感觉所有的jar冲突问题基本都能解决了。