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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

[java进阶]3.slf4j作用及其实现原理

發布時間:2025/3/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [java进阶]3.slf4j作用及其实现原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

參考博客:https://www.cnblogs.com/xrq730/p/8619156.html

1.?簡單回顧門面模式

slf4j是門面模式的典型應用,因此在講slf4j前,我們先簡單回顧一下門面模式,門面模式,其核心為外部與一個子系統的通信必須通過一個統一的外觀對象進行,使得子系統更易于使用。用一張圖來表示門面模式的結構為:

門面模式的核心為Facade即門面對象,門面對象核心為幾個點:

  • 知道所有子角色的功能和責任
  • 將客戶端發來的請求委派到子系統中,沒有實際業務邏輯
  • 不參與子系統內業務邏輯的實現

2.?我們為什么要使用slf4j

我們為什么要使用slf4j,舉個例子:

我們自己的系統中使用了logback這個日志系統 我們的系統使用了A.jar,A.jar中使用的日志系統為log4j 我們的系統又使用了B.jar,B.jar中使用的日志系統為slf4j-simple

這樣,我們的系統就不得不同時支持并維護logback、log4j、slf4j-simple三種日志框架,非常不便。

解決這個問題的方式就是引入一個適配層,由適配層決定使用哪一種日志系統,而調用端只需要做的事情就是打印日志而不需要關心如何打印日志,slf4j或者commons-logging就是這種適配層,slf4j是本文研究的對象。

從上面的描述,我們必須清楚地知道一點:slf4j只是一個日志標準,并不是日志系統的具體實現。理解這句話非常重要,slf4j只做兩件事情:

  • 提供日志接口
  • 提供獲取具體日志對象的方法
  • slf4j-simple、logback都是slf4j的具體實現,log4j并不直接實現slf4j,但是有專門的一層橋接slf4j-log4j12來實現slf4j。為了更理解slf4j,我們先看例子,再讀源碼,相信讀者朋友會對slf4j有更深刻的認識。

    • slf4j應用舉例(moven引入多個日志實現)
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.lineshen.log</groupId><artifactId>log-test</artifactId><version>1.0.0</version><packaging>jar</packaging><name>log-test</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>1.7.25</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.21</version></dependency></dependencies> </project>

    測試代碼:

    public void testSlf4j() {Logger logger = LoggerFactory.getLogger(Object.class);logger.error("123"); }

    接著我們首先把上面pom.xml的log實現方法注釋掉,即不引入任何slf4j的實現類,運行Test方法,這時候會報錯,找不到打印log的實現方法:

    看到沒有任何日志的輸出,這驗證了我們的觀點:slf4j不提供日志的具體實現,只有slf4j是無法打印日志的。

    接著,可以把所有的日志實現方法都打開,測試結果:

    和上面的差別是,可以輸出日志,但是會輸出一些告警日志提示我們同時引入了多個slf4j的實現,然后選擇其中的一個作為我們使用的日志系統。從例子我們可以得出一個重要的結論,即slf4j的作用:只要所有代碼都使用門面對象slf4j,我們就不需要關心其具體實現,最終所有地方使用一種具體實現即可,更換、維護都非常方便

    • slf4j實現原理

    slf4j的用法就是常年不變的一句"Logger logger = LoggerFactory.getLogger(Object.class);",可見這里就是通過LoggerFactory去拿slf4j提供的一個Logger接口的具體實現而已,LoggerFactory的getLogger的方法實現為:

    public static Logger getLogger(Class<?> clazz) {Logger logger = getLogger(clazz.getName());if (DETECT_LOGGER_NAME_MISMATCH) {Class<?> autoComputedCallingClass = Util.getCallingClass();if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),autoComputedCallingClass.getName()));Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");}}return logger; }

    從第2行開始跟代碼,一直跟到LoggerFactory的bind()方法:

    private final static void bind() {try {Set<URL> staticLoggerBinderPathSet = null;// skip check under android, see also// http://jira.qos.ch/browse/SLF4J-328if (!isAndroid()) {staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);}// the next line does the bindingStaticLoggerBinder.getSingleton();INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;reportActualBinding(staticLoggerBinderPathSet);fixSubstituteLoggers();replayEvents();// release all resources in SUBST_FACTORYSUBST_FACTORY.clear();} catch (NoClassDefFoundError ncde) {String msg = ncde.getMessage();if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");Util.report("Defaulting to no-operation (NOP) logger implementation");Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");} else {failedBinding(ncde);throw ncde;}} catch (java.lang.NoSuchMethodError nsme) {String msg = nsme.getMessage();if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {INITIALIZATION_STATE = FAILED_INITIALIZATION;Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");Util.report("Your binding is version 1.5.5 or earlier.");Util.report("Upgrade your binding to version 1.6.x.");}throw nsme;} catch (Exception e) {failedBinding(e);throw new IllegalStateException("Unexpected initialization failure", e);} }

    這個地方第7行是一個關鍵,看一下代碼:

    static Set<URL> findPossibleStaticLoggerBinderPathSet() {// use Set instead of list in order to deal with bug #138// LinkedHashSet appropriate here because it preserves insertion order// during iterationSet<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();try {ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();Enumeration<URL> paths;if (loggerFactoryClassLoader == null) {paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);} else {paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);}while (paths.hasMoreElements()) {URL path = paths.nextElement();staticLoggerBinderPathSet.add(path);}} catch (IOException ioe) {Util.report("Error getting resources from path", ioe);}return staticLoggerBinderPathSet; }

    這個地方重點其實就是第12行的代碼,getLogger的時候會去classpath下找STATIC_LOGGER_BINDER_PATH,STATIC_LOGGER_BINDER_PATH值為"org/slf4j/impl/StaticLoggerBinder.class",即所有slf4j的實現,在提供的jar包路徑下,一定是有"org/slf4j/impl/StaticLoggerBinder.class"存在的,我們可以看一下:

    我們不能避免在系統中同時引入多個slf4j的實現,所以接收的地方是一個Set。大家應該注意到,上部分在演示同時引入logback、slf4j-simple、log4j的時候會有多個日志實現的警告:

    因為有三個"org/slf4j/impl/StaticLoggerBinder.class"存在的原因,此時reportMultipleBindingAmbiguity方法控制臺輸出語句:

    private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {Util.report("Class path contains multiple SLF4J bindings.");for (URL path : binderPathSet) {Util.report("Found binding in [" + path + "]");}Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");} }

    那可能會問,同時存在三個"org/slf4j/impl/StaticLoggerBinder.class"怎么辦?首先確定的是這不會導致啟動報錯,其次在這種情況下編譯期間,編譯器會選擇其中一個StaticLoggerBinder.class進行綁定,這個地方sfl4j也在reportActualBinding方法中報告了綁定的是哪個日志框架:

    private static void reportActualBinding(Set<URL> binderPathSet) {// binderPathSet can be null under Androidif (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");} }

    對照上面的截圖,看最后一行,確實是"Actual binding is of type..."這句。

    最后StaticLoggerBinder就比較簡單了,不同的StaticLoggerBinder其getLoggerFactory實現不同,拿到ILoggerFactory之后調用一下getLogger即拿到了具體的Logger,可以使用Logger進行日志輸出。

    總結

    以上是生活随笔為你收集整理的[java进阶]3.slf4j作用及其实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。

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