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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SLF4J源码分析

發布時間:2023/12/29 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SLF4J源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

介紹

官網:http://www.slf4j.org/

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

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

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

1、為什么要設計出一個日志接口的抽象層?

我們都知道,日志對于一個系統來說非常重要。同樣,我們在開發出了一個庫時,也需要打印一些調試或者運行日志,而我們系統往往會引入大量的第三方庫。這是,就會遇到一個問題:假設我們系統使用的是Log4j日志框架,引入了RMQ庫使用的是Logback框架,這時系統就出現了兩個日志框架,維護起來非常麻煩。

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

如果我們都是通過SLF4J這種統一的接口,那么RMQ庫在發布時就無需帶著具體日志框架的實現,這樣我們系統引入RMQ后,仍然使用的是我們系統中引入的日志實現了,這樣就方便了維護。

slf4j只做兩件事情:

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

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

2、SLF4J和JCL區別:

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

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

推薦使用slf4j中占位符原因主要有兩點:

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

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

3、SLF4J使用:

SLF4J的使用非常簡單:

  • 引入SLF4J依賴 (slf4j-api.jar)
  • 引入一種SLF4J的實現,比如: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將默認為無操作實現;

下圖從SLF4J官網中找到的一個圖,表示了各種實現類和SLF4J的關系:

?

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

靜態綁定原理

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

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

SLF4J相比JCL的一大優勢是采用了靜態綁定,避免了在OSGI等場景中通過classloader動態綁定造成的困擾。

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

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

1.1)源碼分析:

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

1)bind()方法:

  • 調用findPossibleStaticLoggerBinderPathSet()方法獲取classpath上所有的org/slf4j/impl/StaticLoggerBinder.class,用來報告(沒有找到也不會報錯);
  • 執行StaticLoggerBinder.getSingleton()實現靜態綁定,如果沒有日志實現框架,則拋出異常;
  • 執行reportActualBinding()方法
  • ?

    2)findPossibleStaticLoggerBinderPathSet()方法:

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

    ?

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

    ?

    3)日志實現庫:

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

    1.2)疑問:

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

    寫一個工程A,類似sfl4j-api,然后把StaticLoggerBinder類刪掉,工程雖然報錯,但是可以通過mvn install打包成功;

    寫一個工程B,引入A.jar,然后調用其中方法,會發現報錯:Unresolved compilation problem: 從A.jar包中查看相應的LoggerFatory類,居然是這樣的:

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

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

    1.3)總結:

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

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

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

    參考:

    https://juejin.im/post/6844903574116237326

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

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

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

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

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

    說明:(官網)

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

    1.1)源碼分析:

    1)bind方法:

    2)findServiceProviders()方法:

    3)日志實現庫:

    slf4j-api:1.8.0-beta-2版本,對應的logback-classic版本為logback-classic:1.3.0-alpha4。為了兼容1.8的SLF4J,logback-classic提供了SPI服務配置文件,如下圖。這樣,在啟動階段,SLF4J就可以通過ServiceLoader找到logback-classic并進行注冊了。

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

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

    總結:

    slf4j-api1.8版本整個流程和1.7的基本一致,除了采用了更優雅的服務發現機制,在其他方面,SLF4J 1.8與之前版本差別很小。

    參考:

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

    ?

    總結

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

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。