日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SLF4J源码分析

發(fā)布時(shí)間:2023/12/29 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SLF4J源码分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

介紹

官網(wǎng):http://www.slf4j.org/

github:https://github.com/qos-ch/slf4j

SLF4J(Simple Logging Facade for Java),它為Java的日志系統(tǒng)提供了一套統(tǒng)一的接口(門面),即:作為各種日志框架(java.util.logging,logback,log4j)的抽象。

通過引入SLF4J,可以使項(xiàng)目與logging具體的實(shí)現(xiàn)分離,在提供了一致的接口的同時(shí),提供了靈活選擇logging實(shí)現(xiàn)的能力。(引入SLF4J的庫/應(yīng)用意味著僅添加一個(gè)強(qiáng)制性依賴項(xiàng)slf4j-api.jar)

1、為什么要設(shè)計(jì)出一個(gè)日志接口的抽象層?

我們都知道,日志對(duì)于一個(gè)系統(tǒng)來說非常重要。同樣,我們?cè)陂_發(fā)出了一個(gè)庫時(shí),也需要打印一些調(diào)試或者運(yùn)行日志,而我們系統(tǒng)往往會(huì)引入大量的第三方庫。這是,就會(huì)遇到一個(gè)問題:假設(shè)我們系統(tǒng)使用的是Log4j日志框架,引入了RMQ庫使用的是Logback框架,這時(shí)系統(tǒng)就出現(xiàn)了兩個(gè)日志框架,維護(hù)起來非常麻煩。

解決這個(gè)問題的方法是引入一個(gè)適配層。例如:

如果我們都是通過SLF4J這種統(tǒng)一的接口,那么RMQ庫在發(fā)布時(shí)就無需帶著具體日志框架的實(shí)現(xiàn),這樣我們系統(tǒng)引入RMQ后,仍然使用的是我們系統(tǒng)中引入的日志實(shí)現(xiàn)了,這樣就方便了維護(hù)。

slf4j只做兩件事情:

  • 提供日志接口
  • 提供獲取具體日志對(duì)象的方法

說明:這種抽象的思想,在軟件開發(fā)中很常見。

2、SLF4J和JCL區(qū)別:

在SLF4J之前,Apache Common Logging(即Jakarta Commons Logging,簡稱JCL)也提供了類似的功能(即:統(tǒng)一的日志接口)。它與SLF4J的區(qū)別在于:

  • JCL即提供了統(tǒng)一的接口,也提供了一套默認(rèn)的實(shí)現(xiàn);SLF4J則只提供了接口層
  • JCL采用運(yùn)行時(shí)綁定,通過Classloader體系加載相應(yīng)的logging實(shí)現(xiàn);SLF4J采用了靜態(tài)綁定
  • SLF4J在接口易用性上更有優(yōu)勢(shì),大大減少了不必要的日志拼接:
    • JCL為了避免無效的字符串拼接,一般需要通過if判斷:
    • SLF4J則提供了占位符"{}",只在必要的情況下才會(huì)進(jìn)行日志字符串處理和拼接:
//JCL if (log.isInfoEnabled()){log.info("testid:"+id+",cont:"+JSON.toJSONString(jsonstr)); }//slf4j log.info("testid:{},cont:{}",id,JSON.toJSONString(jsonstr));

推薦使用slf4j中占位符原因主要有兩點(diǎn):

  • 當(dāng)設(shè)置的日志級(jí)別高于某條代碼中的日志級(jí)別時(shí),使用占位符可以免掉字符串拼接操作;
  • 占位符底層使用的是StringBuilder進(jìn)行的拼接,性能比“+”要好;

注:在SLF4J和JCL中,推薦使用前者。

3、SLF4J使用:

SLF4J的使用非常簡單:

  • 引入SLF4J依賴 (slf4j-api.jar)
  • 引入一種SLF4J的實(shí)現(xiàn),比如:logback、log4j...

然后:

import org.slf4j.Logger; import org.slf4j.LoggerFactory;public MyClass {Logger logger = LoggerFactory.getLogger(MyClass.class);puhblic void method() {logger.info("hello world...");} }

注:從1.6.0開始,如果在類路徑上未找到綁定,則SLF4J將默認(rèn)為無操作實(shí)現(xiàn);

下圖從SLF4J官網(wǎng)中找到的一個(gè)圖,表示了各種實(shí)現(xiàn)類和SLF4J的關(guān)系:

?

總之,SLF4J接口及其各種適配器非常簡單,不依賴任何類加載器,所以SLF4J不會(huì)遇到類加載器問題或Commons Logging(JCL)所觀察??到的內(nèi)存泄漏。實(shí)際上,每個(gè)SLF4J綁定在編譯時(shí)都進(jìn)行了硬連線,以使用一個(gè)且僅一個(gè)特定的日志記錄框架。

靜態(tài)綁定原理

和Apache Common Logging不同,SLF4j采用了靜態(tài)綁定來確定具體日志庫。靜態(tài)綁定就是:

  • 每一個(gè)具體的日志庫定義一個(gè)包名和類名都相同的類: org.slf4j.impl.StaticLoggerBinder,這個(gè)類的功能就是調(diào)用具體的日志庫,該類存放在Adaptation layer(適配層)或者native implementation of slf4j-api(實(shí)現(xiàn)包)的jar包中;(該類在slf4j-api打成jar包時(shí)被mvn移除)
  • SLF4j的使用者只要把具體日志庫對(duì)應(yīng)的Adaptation layer或者native implementation of slf4j-api的jar包放入classpath中,SLF4j便會(huì)裝載(load)對(duì)應(yīng)版本的org.slf4j.impl.StaticLoggerBinder,從而調(diào)用具體的日志庫;
  • slf4j-api.jar中通過classLoader.getResources("org/slf4j/impl/StaticLoggerBinder.class")來加載classpath中具體的日志庫中的StaticLoggerBinder類;

SLF4J相比JCL的一大優(yōu)勢(shì)是采用了靜態(tài)綁定,避免了在OSGI等場(chǎng)景中通過classloader動(dòng)態(tài)綁定造成的困擾。

參考:https://blog.csdn.net/weixin_34248023/article/details/91891106

1、1.7.25版本的slf4j-api.jar靜態(tài)綁定過程分析:

1.1)源碼分析:

在demo中可知,使用SLF4J的LoggerFactory.getLogger(Class<?>)方法獲取一個(gè)Logger對(duì)象,這個(gè)過程完成了和具體日志實(shí)現(xiàn)類的綁定。通過slf4j-api.jar源碼,SLF4J是調(diào)用bind()方法實(shí)現(xiàn)的綁定。

1)bind()方法:

  • 調(diào)用findPossibleStaticLoggerBinderPathSet()方法獲取classpath上所有的org/slf4j/impl/StaticLoggerBinder.class,用來報(bào)告(沒有找到也不會(huì)報(bào)錯(cuò));
  • 執(zhí)行StaticLoggerBinder.getSingleton()實(shí)現(xiàn)靜態(tài)綁定,如果沒有日志實(shí)現(xiàn)框架,則拋出異常;
  • 執(zhí)行reportActualBinding()方法
  • ?

    2)findPossibleStaticLoggerBinderPathSet()方法:

    通過jdk提供的ClassLoader.getStstemResources()方法獲取指定資源的URI。

    ?

    可以發(fā)現(xiàn),在slf4j-api.jar包中根本沒有org.slf4j.impl.StaticLoggerBinder 這個(gè)類,所以,如果沒有具體的日志實(shí)現(xiàn)庫,那么在執(zhí)行到StaticLoggerBinder.getSingleton()方法時(shí)就會(huì)拋出NoClassDeffoundException

    ?

    3)日志實(shí)現(xiàn)庫:

    slf4j-log4j12庫中的org.slf4j.impl.StaticLoggerBinder

    1.2)疑問:

    通過slf4j源碼,LoggerFactory.java文件有一行import org.slf4j.impl.StaticLoggerBinder; 但是上面我們發(fā)現(xiàn)在slf4j-api.jar中居然沒有該org.slf4j.impl.StaticLoggerBinder類,也就是說slf4j-api這個(gè)工程是無法編譯通過的,又是如何打成slf4j-api.jar的呢?

    寫一個(gè)工程A,類似sfl4j-api,然后把StaticLoggerBinder類刪掉,工程雖然報(bào)錯(cuò),但是可以通過mvn install打包成功;

    寫一個(gè)工程B,引入A.jar,然后調(diào)用其中方法,會(huì)發(fā)現(xiàn)報(bào)錯(cuò):Unresolved compilation problem: 從A.jar包中查看相應(yīng)的LoggerFatory類,居然是這樣的:

    可以發(fā)現(xiàn):雖然上面可以用mvn打包成功,但是由于A工程是一個(gè)編譯有問題的工程,反編譯字節(jié)碼文件可以看到方法全都拋出異常,這說明在打包時(shí),LoggerFactory類生成的字節(jié)碼文件是不完整的,帶有錯(cuò)誤的。

    通過slf4j-api源碼可以發(fā)現(xiàn),其實(shí)在slf4j-api工程中是有org.slf4j.impl.StaticLoggerBinder.java類的,只是在mvn打包的時(shí)候通過ant插件,將org.slf4j.impl.StaticLoggerBinder.class移除掉了。騙過了jdk,使得LoggerFactory.class是一個(gè)完整的,可以校驗(yàn)通過的字節(jié)碼文件。

    1.3)總結(jié):

    先來明確一下 Java 的綁定(Binding)的概念,Java 本身只支持靜態(tài)(static)綁定與運(yùn)行時(shí)(runtime)綁定,直到與 JDK 1.6 版本一起發(fā)布的 JSR269 才能進(jìn)行編譯時(shí)綁定,編譯時(shí)綁定最有代表的是lomok 在編譯過程中修改字節(jié)碼。

    1.7.25版本的SFL4j 的 logger 實(shí)例是 new 出來的(通過StaticLoggerBinder單例),綁定 LogContext 的 StaticLoggerBinder(中介類) 是寫死的,編譯時(shí)并沒有處理任何邏輯,也談不上什么編譯時(shí)綁定,而且翻遍了 SLF4j 文檔也沒有找到任何有關(guān)編譯時(shí)綁定的材料,官方只提到了 “static binding”, 所以,SLF4j使用的是 Convention over Configuration(CoC)– 慣例優(yōu)于配置原則,不管是什么日志框架,只加載org.slf4j.impl.StaticLoggerBinder。這完美契合了軟件設(shè)計(jì)的 KISS(Keep It Simple, Stupid)原則。

    而 Commons-logging 魔法(magic)一樣的動(dòng)態(tài)加載雖然設(shè)計(jì)很高大上,在應(yīng)用領(lǐng)域卻直接被打臉,低效率、與 OSGi 共同使用所導(dǎo)致的 ClassLoader 問題更是火上澆油,所以員外與大家共勉,寫代碼切勿炫技。

    參考:

    https://juejin.im/post/6844903574116237326

    https://www.jianshu.com/p/b562b7ff499f

    2、1.8版本的slf4j-api.jar靜態(tài)綁定過程分析:

    SLF4J 1.8中最大的改進(jìn)就是摒棄了之前的hard code的代碼綁定(要求具體實(shí)現(xiàn)日志框架中必須要有一個(gè)org.slf4j.impl.StaticLoggerBinder.java),而是使用了更加優(yōu)雅、耦合更松的SPI方式進(jìn)行服務(wù)發(fā)現(xiàn)。我們看看1.8版本slf4j-api中對(duì)日志綁定的改進(jìn):

    • 提供了org.slf4j.spi.SLF4JServiceProvider服務(wù)接口用于SPI綁定
    • 改進(jìn)了org.slf4j.LoggerFactory.bind()的實(shí)現(xiàn),采用SPI方式進(jìn)行SLF4JServiceProvider服務(wù)發(fā)現(xiàn)和綁定
    • 不再支持1.8版本以前的按照約定的類型StaticXxxBinder約定類名進(jìn)行綁定的方式

    由此可見,1.8版本和之前的版本是不兼容的(http://www.slf4j.org/codes.html#version_mismatch)。而且1.8往上的版本都是beta,沒有一個(gè)是stable/release的。

    說明:(官網(wǎng))

    從客戶端的角度來看,slf4j-api的所有版本都是兼容的。只需要確保綁定的版本與slf4j-api.jar的版本匹配即可。在初始化時(shí),如果SLF4J懷疑可能存在sfl4j-api與綁定版本不匹配的問題,它將發(fā)出有關(guān)可疑不匹配的警告。

    1.1)源碼分析:

    1)bind方法:

    2)findServiceProviders()方法:

    3)日志實(shí)現(xiàn)庫:

    slf4j-api:1.8.0-beta-2版本,對(duì)應(yīng)的logback-classic版本為logback-classic:1.3.0-alpha4。為了兼容1.8的SLF4J,logback-classic提供了SPI服務(wù)配置文件,如下圖。這樣,在啟動(dòng)階段,SLF4J就可以通過ServiceLoader找到logback-classic并進(jìn)行注冊(cè)了。

    同時(shí),最新版的logback也去掉了org.slf.impl包,徹底摒棄了老版本SLF4J的支持。

    同樣,在slf4j-log4j12-1.8版本中,也是去掉了org.slf.impl包,提供了SPI服務(wù)配置文件:

    總結(jié):

    slf4j-api1.8版本整個(gè)流程和1.7的基本一致,除了采用了更優(yōu)雅的服務(wù)發(fā)現(xiàn)機(jī)制,在其他方面,SLF4J 1.8與之前版本差別很小。

    參考:

    https://www.jianshu.com/p/6cf21fb18639

    ?

    總結(jié)

    以上是生活随笔為你收集整理的SLF4J源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。