前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

1、Maven冲突的原因和解决方案

1.1、原因

1、如果项目的依赖A和依赖B同时引入了依赖C。

2、如果依赖C在A和B中的版本不一致就可能依赖冲突。

3、比如 项目 <- A, B, A <- C(1.0),B <- C(1.1)。

4、那么maven如果选择高版本C(1.1)来导入(这个选择maven会根据不等路径短路径原则和同等路径第一声明原则选取),C(1.0)中的类M在C(1.1)中被修改而不存在了。

5、在运行期,很有可能出现依赖A在执行过程中调用C(1.0)以前有但是升级到C(1.1)就缺失的类M,导致运行期失败,出现很典型的依赖冲突时的NoClassDefFoundError错误。 如果是升级后出现原有的方法被修改而不存在的情况时,就会抛出NoSuchMethodError错误

1.2、解决方案

1、由于B引入的C的版本高而A依赖的C版本低,我们优先会选择兼容高版本C的方案,即试图把A的版本调高以使得引入的依赖C可以和B引入的依赖达到一致的版本,以此来解决依赖冲突。当然这是一个理想状况。

2、如果找到了目前已有的所有的A的版本,均发现其依赖的C没有与B一致的1.1版本,比如A是一个许久未升级的旧项目,那么也可以考虑把B的版本拉低以使得C的版本降到与A一致的1.0版本,当然这也可能会反过来导致B不能正常工作。

上面已经可以看出来解决依赖冲突这件事情并不简单,很难顾及两边

但是很多情况下引入不同版本依赖的很可能超过两方而是更多方(也就是包中有包)。

如果B引入的C使用的功能并不跟被1.1版本抛弃的类或方法有关,而是其他在1.1版本中仍然没有改变的类或方法,那么可以考虑直接使用旧的1.0版本,那么可以使用exclusion标签来在B中排除掉对C的依赖,那么B在使用到C的功能时会使用A引入的1.0旧版本C。即B其实向A妥协使用了A依赖的C。

2、冲突解决

2.1、日志冲突

1、报错如下

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/HealerJean/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/HealerJean/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
Exception in thread "main" java.lang.StackOverflowError
	at org.apache.logging.log4j.util.StackLocator.getCallerClass(StackLocator.java:112)
	at org.apache.logging.log4j.util.StackLocator.getCallerClass(StackLocator.java:125)
	at org.apache.logging.log4j.util.StackLocatorUtil.getCallerClass(StackLocatorUtil.java:55)

2、全局搜索类名StaticLoggerBinder ,发现 有两个

1584412717572

3、依次打开着两个类,他们两兄弟竟然虽然不是同一个 jar 包,但是包路径和名称都一模一样!!!

logback-classic-1.2.3.jar

1584412813019

log4j-slf4j-impl-2.10.0.jar

1584412836757

4、查看他们这两个jar包分别在哪里引用到,借助maven help插件

搜索logback-classic,然后Jump To Source(跳转到源文件处),发现它是SpringBoot parent的一部分,而我们正是要用logback打印,所以这个包使我们想要的

1584413060320

搜索log4j-slf4j-impl,发现它是log4j的子包,而我们正是要排除它,

1584413199855


<!--log4j2依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

5、排除方法

1584413437855

<!--log4j2依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
        </exclusion>
    </exclusions>
</dependency>

6、冲突原因

从代码中了解到,如果某个名字的类被加载后,类加载器是不会再重新加载,所以我们的问题根本原因可以是出现在 ,先加载了 org.slf4j 包的 org.slf4j.impl.StaticLoggerBinder,同名的 ch.qos.logback 包下的 StaticLoggerBinder 类没有被加载,按照字母的顺序加载JAR文件。有了这个类以后,后面的类则不会加载了

ContactAuthor