目录
第三类:spring使用获取当前类名的方式,全限定名不同,但bean名相同
方法四:找到异常指向的问题类名,然后在项目代码中全局搜索类名,看有多少个相同的bean名然后处理
一.jar包冲突的 本质
Java 应用程序因某种因素,加载不到正确的类而导致其行为跟预期不一致。
二. jar包冲突的四种情况
第一类jar包冲突问题(同一jar包版本不同)
- 应用程序依赖的同一个 Jar 包出现了多个不同版本,并选择了错误的版本而导致 JVM 加载不到需要的类或加载了错误版本的类。
- 出现该问题的三个必要条件:
- 依赖树中出现了同一个jar包的多个版本。
- 该jar包的多个版本之间接口发生了变化(类名,方法签名变化,方法行为变化)
- maven 的仲裁机制选择了错误的版本
第二类jar包冲突问题(不同jar包的同一类版本不同)
- 同样的类(类的全限定名完全一样)出现在多个不同的依赖 Jar 包中,即该类有多个版本,并由于 Jar 包加载的先后顺序(Maven的路径最短和覆盖优先原则)导致 JVM 加载了错误版本的类。如:假设有 A 、 B 、 C 三个jar包,由于 Jar 包依赖的路径长短、声明的先后顺序或 文件系统 的文件加载顺序等原因, 类加载器 首先从 Jar 包 A 中加载了该类后,就不会加载其余 Jar 包中的这个类了。
- 出现该问题的三个必要条件:
- 同一类M出现在了两个(或两个以上)不同的jar包A、B中。
- 类M在A、B中有差异,行为不同。
- 加载的类M不是我们想要的。
第三类:spring使用获取当前类名的方式,全限定名不同,但bean名相同
相同的bean名,不同的包名,在使用的时候也没有指定全限定名,spring提供两种beanName生成策略,基于注解的spring-boot默认使用的是AnnotationBeanNameGenerator,它生成beanName的策略就是,取当前类名(不是全限定类名FullyQualifiedAnnotationBeanNameGenerator)作为beanName。由此,如果出现不同包结构下同样的类名称,肯定会出现冲突导致容器启动报错。
三.解决方案
方法一:手动排查
- 根据异常堆栈信息确定导致冲突的类名。
- 通过mvn dependency:tree -Dverbose -Dincludes=: 查看是哪个地方引入的jar包的版本。
- 如果是第一类 Jar 包冲突,则可用 排除不需要的 Jar 包版本或者在依赖管理中声明版本。
- 如果是第二类Jar包冲突,如果可以排除,则用 排掉不需要的那个 Jar 包,若不能排,则需考虑 Jar 包的升级或换个别的 Jar 包。
方法二:利用maven helper插件
可以识别为了解决 groupId 和 artifactId 完全相同的依赖冲突的工具
方法三: 利用jvm参数辅助-verbose:class
显示类加载的顺序,查看加载的先后顺序,并检查bean加载的是来自于哪个包
方法四:找到异常指向的问题类名,然后在项目代码中全局搜索类名,看有多少个相同的bean名然后处理
方法五:利用maven的依赖原则解决版本冲突
在maven声明中缩短路径-若路径长度不同 ---利用最短路径优先原则
在maven声明中靠前声明-若路径长度相同 ---利用最先声明优先原则