解决Gradle传递性依赖冲突

sorra 发表于 04/09 14:28 修改于 04/10 12:28 阅读数432

问题

今天忽然发现轻境界无法输出日志了,控制台输出以下警告⚠️:

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details.
SLF4J: Class path contains SLF4J bindings targeting slf4j-api versions prior to 1.8.
SLF4J: Ignoring binding found at [jar:file:/Users/dhu/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.1.11/ccedfbacef4a6515d2983e3f89ed753d5d4fb665/logback-classic-1.1.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#ignoredBindings for an explanation.

看来是SLF4J的依赖出问题了。问题是我没修改任何依赖啊!什么都没改,也会出问题?

探索

先排查吧。点击SLF4J的官网链接,据文档所述,这是因为slf4j-api 1.8不兼容旧版logback-classic库。

小知识:SLF4J是一个日志接口库,Logback是一个日志实现库,logback-classic能桥接SLF4J和Logback。

小知识:传递性依赖是指项目所直接依赖的库又间接依赖了更多的库,例如Spring依赖CGLib。

IDE中的Gradle依赖树显示我依赖了slf4j-api 1.8-alpha0和logback-classic 1.1.11。但是我明确记得没依赖过alpha版本的slf4j-api,而这个版本需要Logback 1.2.x才能支持!现在引用的Logback 1.1.11是不行的!

在项目中只有Spring Boot和Ebean ORM依赖于SLF4J,因为上周升级了Spring Boot,所以我先怀疑Spring Boot——我尝试排除它对slf4j-api和logback-classic的传递性依赖,对build.gradle修改代码如下:

compile ("org.springframework.boot:spring-boot-starter-aop:${springBootVersion}") {
  exclude group: 'org.slf4j', module: 'slf4j-api'
  exclude group: 'ch.qos.logback', module: 'logback-classic'
}

然并卵,控制台仍然输出警告。

解决

我使用Gradle的依赖分析功能,输入命令./gradlew dependencyInsight --dependency slf4j-api,输出如下:

before

注意看最后一段:org.slf4j:slf4j-api:[1.7.12,) -> 1.8.0-alpha0,这是罪魁祸首!它是被avaje-ebeanorm间接引用进来的,是一个传递性依赖!

它的依赖区间是[1.7.12,),没有上限,意思是凡是1.7.12版本以上的都可以,这就导致——当slf4j-api发布了1.8.0-alpha0之后,如果Gradle恰好联网查了一下,就会使用最新版的1.8.0-alpha0!

在一个项目中,如果同时存在高版本依赖和低版本依赖,高版本就会覆盖低版本。虽然其他的依赖项指定了依赖区间的上限,但是只要有一个奇怪的依赖混进来引用了更高版本的库,就会覆盖其他所有的低版本。

于是我排除avaje-ebeanorm引入的间接依赖,修改build.gradle如下:

compile ('org.avaje.ebeanorm:avaje-ebeanorm:7.9.1') {
  exclude group: 'org.slf4j', module: 'slf4j-api'
  exclude group: 'ch.qos.logback', module: 'logback-classic'
}

再次运行依赖分析命令./gradlew dependencyInsight --dependency slf4j-api,输出如下:

after

正常了!

以后再遇这种问题,第一时间用Gradle的dependencyInsight功能,棒!