作为java程序员,我们在项目开发的过程中,肯定需要依赖大量的第三方依赖包,通常我们都是使用maven构建工具来管理第三方工具包的,既然有依赖第三方包,那么肯定也会遇到jar包冲突的问题,那这个时候,我们就需要定位问题所在,定位问题所在首先需要理解冲突的原理。
1. 下面首先讲解maven中如何引入jar包,通过maven的坐标引入jar包
2. maven jar包的依赖的原理
maven中jar包的依赖是传递依赖,以上图为例,maven在解析zookeeper的包时,不但会解析zookeeper包,可能还会解析zookeeper内部依赖的包,可能还会解析zookeeper内部依赖的包所依赖的包,依赖关系不断传递下去,直至没有依赖关系,我们在maven中打开zookeeper的pom,我们可以看到zookeeper还依赖了slf4j-api的jar包,commons-collections包
3. maven中jar包冲突产生的原因
maven 处理依赖冲突的方式:
若项目中多个Jar同时引用了相同的Jar时,会产生依赖冲突,但Maven采用了两种避免冲突的策略,因此在Maven中是不存在依赖冲突的。
3.1 短路优先
本项目——>A.jar——>B.jar——>X.jar
本项目——>C.jar——>X.jar
若本项目引用了A.jar,A.jar又引用了B.jar,B.jar又引用了X.jar,并且C.jar也引用了X.jar。
在此时,Maven只会引用引用路径最短的Jar。
3.2 声明优先
若引用路径长度相同时,在pom.xml中谁先被声明,就使用谁。
3.3 假设 有以下两个依赖关系:其中C1和C2是两个相同的jar包,他们只是version版本号不同
A->B->C1 依赖关系: A包依赖B包,B包依赖C1包
E->D->C2 依赖关系: E包依赖D包,D包依赖C2包
当我们在项目中同时引入了A包和E包,按照maven的依赖传递原理,那么我们相当于在项目中同时引入了两个C包,只是他们的version版本号不同,这时候就会出现冲突了,如果我们调用了C2中一个method方法,这个方法在C1中不存在(可能是C2高版本升级之后新添加的方法),那么jvm在进行类加载的时候,加载按照A->B->C1顺序,这个时候就会报NoSuchMethodError的错误,jar包冲突问题就出现了
下面举个例子:借助于maven-helper 这个插件
上图中红色标注的就是冲突的地方:commons-collections 3.2.1 这个版本的依赖包产生冲突,冲突的地方在velocity-tools中,为了验证冲突的位置是否是在velocity-tools这里,我们打开All Dependencies as tree这里
查看上面的图,我们可以看到,有两个版本的commons-collections 依赖包,另外一个版本号是3.2,所以就产生jar包冲突了。
4. maven中jar包产生冲突之后的解决方案
既然有多个版本号的jar依赖存在,那我们就自然而然的想到,移除多余的jar包,只保留一个不就可以了,事实确实如此,我们可以通过移除内部依赖的jar包达到解决jar包冲突的目的。
4.1 移除的方式有两种,第一种是借助于maven helper插件中的Dependency Analyzer分析冲突的jar包,然后在提示冲突的jar包上面右击Exclude,然后刷新就可以了
从上图中可以看出,commons-collections 这个冲突的jar已经不存在了
4.2 手动移除,这个是通过在pom中添加<exclusion>标签达到解决jar包冲突的目的
5. 为了减少jar包版本冲突问题,一般我们需要将这个被多模块引用的jar包统一管理起来,使用<dependencyManagement>在父的pom中声明,然后多个子的模块中引用,这个时候多个子的module中是不需要声明版本号的。
举个例子:
这是个多module项目,我们在父的pom中声明了一个ghub-commons jar包,然后我们在子的module中直接引用即可。
上面所讲的可能只是解决包冲突的部分方案,具体的问题可能需要具体的去看待,问题不一样,解决方案就不一样。