转载自:https://blog.csdn.net/q15102780705/article/details/120763966
=========================================================
jar包冲突产生的原因
举例说明:
- 依赖链路一:A -> B -> C -> G21(guava 21.0)
- 依赖链路二:D -> F -> G20(guava 20.0)
假设项目中同时引入了A和D的依赖,按照依赖传递机制和默认依赖调节机制(第一:路径最近者优先;第二:第一声明优先),默认会引入G20版本的jar包,而G21的jar包不会被引用。
如果C中的方法使用了G21版本中的某个新方法(或类),由于Maven的处理,导致G21并未被引入。此时,程序在调用对应类时便会抛出ClassNotFoundException异常,调用对应方法时便会抛出NoSuchMethodError异常。
排查定位jar包冲突
在高版本的IDEA中已经自带了Maven依赖管理插件,依次执行:打开pom.xml文件,在文件内右击,选择Maven,选择Show Dependencies即可查看Maven的依赖层级结构:
执行之后展示的效果便是最开始的spring-boot-web那样效果,在图中可以清楚的看到都使用了哪些依赖,它们的层级,是否有冲突的jar包等。冲突部分会用红色标出,同时标出Maven默认选择了哪个版本。
如果你的IDEA版本中默认没有Maven管理插件,也可安装Maven Helper,通过这块插件来帮你分析jar包冲突。
安装完插件,重启之后,打开pom.xml文件,在文件下面的Dependency Analyzer视图中便可以看到jar包冲突的结果分析:
此时,关于哪些jar包冲突了,便一目了然。同时,可以右击冲突的jar包,执行”Exclude“进行排除,在pom.xml中便会自动添加排除jar包的属性。
对于本地环境可以利用Maven Helper等插件来解决,但在预生产或生成环境中就没那么方便了。此时可以通过mvn命令来定位突出的细节。
执行如下mvn命令:
mvn dependency:tree -Dverbose
注意不要省略-Dverbose,要不然不会显示被忽略的包。
执行结果如下:
通过这种形式,也可以清晰的看出哪些jar包发生了冲突。
如何统一jar包依赖
像上面截图所示,如果一个项目有N多个子项目构成,项目之间可能还有依赖关系,jar包冲突不可避免,此时可采用父pom,统一对版本进行管理,一劳永逸。
通常做法,是在parent模块的pom文件中尽可能地声明所有相关依赖jar包的版本,并在子pom中简单引用(不再指定版本)该构件即可。
比如在父pom.xml中定义Lombok的版本:
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.18.10</version>
- </dependency>
- </dependencies>
- </dependencyManagement>
在子module中便可定义如下:
- <dependencies>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- </dependencies>
通过这种方式,所有的子module中都采用了统一的版本。版本锁定后则不考虑依赖的声明顺序或依赖的路径,以锁定的版本的为准添加到工程中, 此方法在企业开发中常用。
如下的配置是锁定了spring-beans和spring-context的版本:
注意:在工程中锁定依赖的版本并不代表在工程中添加了依赖, 如果工程需要添加锁定版本的依赖则需要单独添加标签, 如下:
上边添加的依赖并没有指定版本, 原因是已在中锁定了版本,所以在下不需要再指定版本。
解决jar包冲突的方法
这里基于Maven项目介绍几种场景下解决Jar冲突的方法:
Maven默认处理:采用此种方法,要牢记Maven依赖调节机制的基本原则,路径最近者优先和第一声明优先;
排除法:上面Maven Helper的实例中已经讲到,可以将冲突的jar包在pom.xml中通过exclude来进行排除;
版本锁定法:如果项目中依赖同一jar包的很多版本,一个个排除非常麻烦,此时可用版本锁定法,即直接明确引入指定版本的依赖。根据前面介绍Maven处理jar包基本原则,此种方式的优先级最高。这种方法一般采用上面我们讲到的如何统一jar包依赖的方式。
jar包冲突的本质:Java应用程序因某种因素,加载不到正确的类而导致其行为跟预期不一致。
具体分两种情况:
情况一:项目依赖了同一jar包的多个版本,并且选错了版本;
情况二:同样的类在不同的jar包中出现,导致JVM加载了错误的类;
情况一,也是本文重点讨论的场景,也就是说引入了多个jar包版本,不同的jar包版本有不同的类和方法。由于(不懂)Maven依赖树的仲裁机制导致Maven加载了错误的jar包,从而导致jar包冲突;
情况二,同一类在不同的jar包中出现。这种情况是由于JVM的同一个类加载器对于同一个类只会加载一次,现在加载一个类之后,同全限定名的类便不会进行加载,从而出现jar包冲突的问题。
针对第二种情况,如果不是类冲突抛出了异常,你可能根本意识不到,所以就显得更为棘手。这种情况就可以采用前文所述的通过分析不同类加载器的优先级及加载路径、文件系统的文件加载顺序等进行调整来解决。
原文地址:https://www.cnblogs.com/hd92/p/16286169.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。