⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 jitwxs.cn/e2390047.html 「Jitwxs」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

一、前言

在进行 Java 开发时,通常我们会选择 Slf4j 作为日志门面,但日志实现却不尽相同。如果系统运行中同时存在多个日志实现,就会出现类似下图的 Warning。

图片

二、问题原因

我们知道 SpringBoot 默认使用的日志实现是 Logback,因此我们尝试在项目中引入 Log4j 的依赖时,就复现了上图的报错。

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

上图报错告知我们存在多个 SLF4J bingdings,分别位于 logback 和 log4j 包中,有两个 StaticLoggerBinder。

我们知道使用 Slf4j ,需要 LoggerFactory.getLogger() 方法获取实例。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private final Logger logs = LoggerFactory.getLogger(xxx.class);

我们就可以通过这个作为入口,去看看源码的实现。如下图所示,我标注了需要关注的核心代码。

  • (1)调用 getILoggerFactory() 方法得到 LoggerFactory。
  • (2)对于首次调用,INITIALIZATION_STATE 应该是 UNINITIALIZED,所以进入初始化的逻辑,调用方法 performInitialization()。
  • (3)调用 bind() 方法。
  • (4)如果不是 isAndroid(),调用 findPossibleStaticLoggerBinderPathSet() 方法,故名思意,查找可能的 staticLoggerBinder,注意这里返回的类型是 SET,即可能是多个。
  • (5)在findPossibleStaticLoggerBinderPathSet() 这个方法内,首先通过 classLoader 加载了 org/slf4j/impl/StaticLoggerBinder.class 这个类的 path,它可能存在多个,因此使用了 while 获取了所有的 path,并最终返回。

图片

  • (6)reportActualBinding() 方法会校验 SET 的 size,如果大于 1,就会打印出一开始我们看见的 Warning 了。

图片

三、问题解决

解决思路就是将你不想要的日志实现从依赖包中排除掉即可,通过 IDEA 提供的 Diagrams 能够非常方便的查看项目中的依赖关系。

打开项目的 POM 文件,右键选择 Diagrams -> Show Dependencies图片

假设我们想要排除 logback 依赖,使用 log4j。Ctrl + F 搜索 logback,可以找到引用该依赖的树形结构。图片

点击窗口左上角的下图中的这个图标,可以只看当前选中的这个依赖的关系。

图片

选中后效果如下:

图片

如上图所示,logback 由 spring-boot-starter-logging 引入,最顶层是由 spring-boot-starter-web 和 spring-boot-starter-test 引入。

我们尝试在 spring-boot-starter-web 中排除该依赖,应该就可以了。如果排出后重新搜索仍然存在 logback 依赖,则重复执行排除的操作。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

四、总结

日志框架冲突特别对于新手来说处理起来比较头疼,因为涉及到了日志接口和日志实现。

我们推崇的应该是面向接口编程,因此我们大到开源项目,小到公司的公共 jar 包,应当合理利用 Maven 的传递机制。具体的日志实现不应该传递出去,避免影响到调用的下游方。

<optional>true</optional>

文章目录
  1. 1. 一、前言
  2. 2. 二、问题原因
  3. 3. 三、问题解决
  4. 4. 四、总结