首页>>后端>>java->SLF4J:我这可是日志的门面啊

SLF4J:我这可是日志的门面啊

时间:2023-12-05 本站 点击:0

众所周知,slf4j 是日志的门面。在阿里的《Java 开发手册》中有相关的记载:

那么,他是如何完成这个事情的呢?

根据官网的介绍,我们跑个 Demo

HelloWorld.java

import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 日志测试类 * * @author xisha * @date 2022/5/9 7:33 下午 */public class HelloWorld {    public static void main(String[] args) {        Logger logger = LoggerFactory.getLogger(HelloWorld.class);        logger.info("Hello World");    }}

pom.xml

<dependencies>        <dependency>            <groupId>org.slf4j</groupId>            <artifactId>slf4j-simple</artifactId>            <version>1.7.36</version>        </dependency>    </dependencies>

运行结果

注意,这个地方除了依赖 slf4j-simple 之外,还可以依赖 slf4j-log4j12 、slf4j-reload4j,slf4j-jdk14,slf4j-nop,slf4j-jcl,logback-classic 等等,相当于每个 依赖都是一个实现。

下载源码,可以看到每个model 都依赖了 slf4j-api

官网上的图也表明 slf4j-api 应该是个核心包

从上面两个图,再根据我多年来写bug的经验盲猜一波,这这个应该是有个什么什么东西注册上去了,然后 LoggerFactory 去拿的时候再把这个注册过的 Logger 拿出来

打个断点进去,那么很明显

StaticLoggerBinder --> JVM 注册LogFactory --> 反射出 StaticLoggerBinder通过 StaticLoggerBinder 获取 ILoggerFactory通过 ILoggerFactory 获取 Logger

代码很简单,我就不帖了,听上去很简单啊这,你以为故事到这里就结束了么? 桥豆麻袋!StaticLoggerBinder 是在哪里定义的? 可以看到,StaticLoggerBinder 在 slf4j-api 是有定义的,且每个实现里也都定义了一个。那么问题来了,我同时引入了 slf4j-api 和一个具体的实现,这玩意儿难道不会冲突么?

但是在我的 Demo 工程里搜的的话,只有实现包里有 StaticLoggerBinder 这个类。哦豁?这是什么骚操作?slf4j-api 里的 StaticLoggerBinder 去哪里了呢?还是说我的源码下载的有问题?

奥~ 看起来好像是被这个框架给删掉了

但是这是个啥玩意儿?

一番搜索之后了解到这是个ant的插件,phase 表示的是生命周期,这个生命周期我就不展开了(毕竟我也不大懂),我们只需要知道 process-classes 是在 compile 之后执行的,这段代码的含义大概是 处理 classes 的时候(在编译之后),把 target/classes/org/slf4j/impl 目录下的文件都删掉的意思。

而我们的 slf4j-api 里的 StaticLoggerBinder ,也正好在这个包下面,你说巧不巧? 狗头.jpg

删掉之后,具体实现类里的相关实现就能趁虚而入,占领高地了。学到了学到了。

你以为故事到这里就结束了么?

你看红框框起来的那行代码

 // the next line does the bindingStaticLoggerBinder.getSingleton();

其他的代码都很好理解,但是这个是个什么鬼……

get 了一个单例,返回值也没用上,这个又是为啥呢,怎么就 dose the binding 了呢?这里就涉及到亿点点的类加载知识了。首先我们看一段Demo代码

// StatisticClass.javapublic class StatisticClass {    static {        // 输出1        System.out.println("StatisticClass init!");    }    private static final StatisticClass SINGLETON = new StatisticClass();    public static StatisticClass getSingleton() {        return SINGLETON;    }    private StatisticClass() {    }}// Demo.javapublic class Demo {    public static void main(String[] args) {        // 注释一        // StatisticClass statisticClass;        // 注释二        //StatisticClass.getSingleton();    }}

来一道面试题,问在不打开注释一和注释二,只打开注释一,只打开注释二的三种情况下,输出1的结果分别是怎么样的?

如果你看过《深入理解java虚拟机》的话,对这个问题应该并不陌生,答案我就不贴了,简单说就是:我们的静态变量 SINGLETON = new StatisticClass() 应该是在类加载的初始化这步完成的。但是如果没有地方显示的执行 StatisticClass 的方法,那么StatisticClass是不会被初始化的。

从上面的例子我们就可以理解 slf4j 里那句the next line does the binding 的含义了。

到这里似乎问题就结束了。但是这里又又又有个问题啊,这里他为啥不用接口呢?就 StaticLoggerBinder 这个类,为啥不设计成接口呢?这里明显是搞成接口更合理啊,想到这里,我是不是可以提一个issue,然后改吧改吧,混一波知名项目的PR?

太简单了少年……看最新的分支,这个确实已经设计成接口了,代码也很简单,我就不贴了,大概是下面这么个逻辑:

之前的findClass 也改成了如下代码的 findServiceProviders:

    private static List<SLF4JServiceProvider> findServiceProviders() {        ServiceLoader<SLF4JServiceProvider> serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class);        List<SLF4JServiceProvider> providerList = new ArrayList<SLF4JServiceProvider>();        for (SLF4JServiceProvider provider : serviceLoader) {            providerList.add(provider);        }        return providerList;    }

奇奇怪怪的 StaticLoggerBinder.getSingleton(); 也改成了 PROVIDER.initialize();

好了,故事到这里就大概结束了,虽然给知名项目提PR的机会没有了,但是好歹是学到了一波知识,这波不亏,不亏不亏~

原文:https://juejin.cn/post/7098185851347140639


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/12228.html