最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談slf4j中的橋接器是如何運(yùn)作的

 更新時(shí)間:2020年12月03日 12:17:31   作者:程序新視界  
這篇文章主要介紹了slf4j中的橋接器是如何運(yùn)作的,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

閱讀分析 slf4j 的日志源碼,發(fā)現(xiàn)其中涵蓋了許多知識(shí)點(diǎn)和優(yōu)秀的設(shè)計(jì),關(guān)鍵它們都是活生生的實(shí)踐案例。寫成系列文章與大家分享,歡迎持續(xù)關(guān)注。

前言

在日志框架 slf4j 中有一組項(xiàng)目,除了核心的 slf4j-api 之外,還有 slf4j-log4j12、slf4j-jdk14 等項(xiàng)目。這一類項(xiàng)目統(tǒng)稱橋接器項(xiàng)目,針對(duì)不同的日志框架有不同的橋接器項(xiàng)目。

在使用 logback 日志框架時(shí),并沒有針對(duì)的橋接器,這是因?yàn)?logback 與 slf4j 是一個(gè)作者所寫,在 logback 中直接實(shí)現(xiàn)了 slf4j 的 SPI 機(jī)制。

但如果使用其他日志框架,那么就必須要用到橋機(jī)器相關(guān)依賴。比如,當(dāng)我們基于 log4j 使用 slf4j 時(shí),除了需要引入 log4j 的 jar 包依賴,還需要引入 slf4j 的下面兩個(gè)依賴:

<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
</dependency>

slf4j-api 為核心依賴,必須引入,而 slf4j-log4j12 就是橋接器用來在 slf4j 和 log4j 之間進(jìn)行過渡和封裝。下面,我們就聊聊橋接器項(xiàng)目的核心實(shí)現(xiàn)。

slf4j-log4j12 橋接器的價(jià)值

要了解橋接器的運(yùn)作,首先需要回顧一下 slf4j 的 SPI 機(jī)制。在我們通過 LoggerFactory.getLogger(Foo.class); 時(shí),slf4j 會(huì)通過 SPI 機(jī)制尋找并初始化 SLF4JServiceProvider 的實(shí)現(xiàn)類。

然后,通過 SLF4JServiceProvider 的實(shí)現(xiàn)類來獲取日志相關(guān)的具體工廠類對(duì)象,進(jìn)而進(jìn)行日志功能的處理。先來看一下 SLF4JServiceProvider 的接口定義:

public interface SLF4JServiceProvider {

  /**
   * 返回ILoggerFactory的實(shí)現(xiàn)類,用于LoggerFactory類的綁定
   */
  ILoggerFactory getLoggerFactory();

  /**
   * 返回IMarkerFactory實(shí)例
   */
  IMarkerFactory getMarkerFactory();

  /**
   * 返回MDCAdapter實(shí)例
   */
  MDCAdapter getMDCAdapter();

  /**
   * 獲取請(qǐng)求版本
   */
  String getRequesteApiVersion();

  /**
   * 初始化,實(shí)現(xiàn)類中一般用于初始化ILoggerFactory等
   */
  void initialize();
}

SLF4JServiceProvider 接口是在 slf4j-api 中定義的,具體的實(shí)現(xiàn)類由其他日志框架來完成。但是像 log4j(logback“敵對(duì)陣營(yíng)”)是不會(huì)在框架內(nèi)實(shí)現(xiàn)該接口的。那么,怎么辦?

針對(duì)此問題,slf4j 提供了 slf4j-log4j12 這類橋接器的過渡項(xiàng)目。在其中實(shí)現(xiàn) SLF4JServiceProvider 接口,并對(duì) Log4j 日志框架接口進(jìn)行封裝,將 Logger(slf4j) 接收到的命令全部委托給 Logger(log4j) 去完成,在使用者無感知的情況下完成偷天換日。

slf4j-log4j12 的核心實(shí)現(xiàn)類

理解了橋接器的存在價(jià)值及原理,下面就來看看 slf4j-log4j12 是如何實(shí)現(xiàn)這一功能的。

首先來看看核心實(shí)現(xiàn)類之一 Log4j12ServiceProvider。它實(shí)現(xiàn)了 SLF4JServiceProvider 接口,主要功能就是完成接口中定義的相關(guān)工廠接口的實(shí)現(xiàn)。源代碼如下:

public class Log4j12ServiceProvider implements SLF4JServiceProvider {

  public static String REQUESTED_API_VERSION = "1.8.99"; 

  private ILoggerFactory loggerFactory; 
  private IMarkerFactory markerFactory; 
  private MDCAdapter mdcAdapter;
  
  public Log4j12ServiceProvider() {
    try {
      @SuppressWarnings("unused")
      Level level = Level.TRACE;
    } catch (NoSuchFieldError nsfe) {
      Util.report("This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version");
    }
  }

  @Override
  public void initialize() {
    loggerFactory = new Log4jLoggerFactory();
    markerFactory = new BasicMarkerFactory();
    mdcAdapter = new Log4jMDCAdapter();
  }
  
  @Override
  public ILoggerFactory getLoggerFactory() {
    return loggerFactory;
  }

  @Override
  public IMarkerFactory getMarkerFactory() {
    return markerFactory;
  }

  @Override
  public MDCAdapter getMDCAdapter() {
    return mdcAdapter;
  }

  @Override
  public String getRequesteApiVersion() {
    return REQUESTED_API_VERSION;
  }
}



該類的實(shí)現(xiàn)看起來很簡(jiǎn)單,構(gòu)造方法中通過嘗試使用 log4j 的 Level.TRACE 調(diào)用來驗(yàn)證 log4j 的版本是否符合要求。log4j1.2.12 之前并沒有 Level.TRACE,所以會(huì)拋出異常,并打印日志信息。不得不贊嘆作者在此處檢查版本的巧妙用法。

而這里對(duì)接口中返回的實(shí)現(xiàn)類主要通過 initialize() 方法來實(shí)現(xiàn)的。這里我們重點(diǎn)看 Log4jLoggerFactory 類的實(shí)現(xiàn)。

public class Log4jLoggerFactory implements ILoggerFactory {

  private static final String LOG4J_DELEGATION_LOOP_URL = "http://www.slf4j.org/codes.html#log4jDelegationLoop";

  // check for delegation loops
  static {
    try {
      Class.forName("org.apache.log4j.Log4jLoggerFactory");
      String part1 = "Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. ";
      String part2 = "See also " + LOG4J_DELEGATION_LOOP_URL + " for more details.";

      Util.report(part1);
      Util.report(part2);
      throw new IllegalStateException(part1 + part2);
    } catch (ClassNotFoundException e) {
      // this is the good case
    }
  }

  ConcurrentMap<String, Logger> loggerMap;

  public Log4jLoggerFactory() {
    loggerMap = new ConcurrentHashMap<>();
    // force log4j to initialize
    org.apache.log4j.LogManager.getRootLogger();
  }

  @Override
  public Logger getLogger(String name) {
    Logger slf4jLogger = loggerMap.get(name);
    if(slf4jLogger != null) {
      return slf4jLogger;
    } else {
      org.apache.log4j.Logger log4jLogger;
      if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME)) {
        log4jLogger = LogManager.getRootLogger();
      } else {
        log4jLogger = LogManager.getLogger(name);
      }

      Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
      Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
      return oldInstance == null ? newInstance : oldInstance;
    }
  }
}

在 Log4j12ServiceProvider 中進(jìn)行了 Log4jLoggerFactory 的實(shí)例化操作,也就直接 new 出來一個(gè)對(duì)象。我們知道,在 new 對(duì)象執(zhí)行會(huì)先執(zhí)行 static 代碼塊,本類的靜態(tài)代碼塊的核心工作就是檢查依賴文件中是否同時(shí)存在反向橋接器的依賴。

其中,org.apache.log4j.Log4jLoggerFactory 是反向橋接器 log4j-over-slf4j 項(xiàng)目中的類,如果加裝到了,說明存在,則拋出異常,打印日志信息。此處再次贊嘆作者運(yùn)用的技巧的巧妙。

在 Log4jLoggerFactory 的構(gòu)造方法中,做了兩件事:第一,初始化一個(gè) ConcurrentMap 變量,用于存儲(chǔ)實(shí)例化的 Logger;第二,強(qiáng)制初始化 log4j 的組件,其中強(qiáng)制初始化 log4j 的組件是通過 getRootLogger 方法,來初始化一些靜態(tài)的變量。

構(gòu)造方法時(shí)初始化了 ConcurrentMap 變量,在 Log4jLoggerFactory 實(shí)現(xiàn)的 getLogger 方法中,先從 Map 中獲取一下是否存在對(duì)應(yīng)的 Logger,如果存在直接返回,如果不存在則進(jìn)行構(gòu)造。而構(gòu)造的 Log4jLoggerAdapter 類很顯然使用了適配器模式,它內(nèi)部持有了 log4j 的 Logger 對(duì)象,自身又實(shí)現(xiàn)了 slf4j 的 Logger 接口。

下面看一下 Log4jLoggerAdapter 的部分代碼實(shí)現(xiàn):

public final class Log4jLoggerAdapter extends LegacyAbstractLogger implements LocationAwareLogger, Serializable {

  final transient org.apache.log4j.Logger logger;

  Log4jLoggerAdapter(org.apache.log4j.Logger logger) {
    this.logger = logger;
    this.name = logger.getName();
    traceCapable = isTraceCapable();
  }

  @Override
  public boolean isDebugEnabled() {
    return logger.isDebugEnabled();
  }


  @Override
  public void log(Marker marker, String callerFQCN, int level, String msg, Object[] arguments, Throwable t) {
    Level log4jLevel = toLog4jLevel(level);
    NormalizedParameters np = NormalizedParameters.normalize(msg, arguments, t);
    String formattedMessage = MessageFormatter.basicArrayFormat(np.getMessage(), np.getArguments());
    logger.log(callerFQCN, log4jLevel, formattedMessage, np.getThrowable());
  }

  public void log(LoggingEvent event) {
    Level log4jLevel = toLog4jLevel(event.getLevel().toInt());
    if (!logger.isEnabledFor(log4jLevel))
      return;

    org.apache.log4j.spi.LoggingEvent log4jevent = toLog4jEvent(event, log4jLevel);
    logger.callAppenders(log4jevent);

  }
  
  // 省略其他方法
}

源碼中,通過構(gòu)造方法傳入 log4j 的 Logger 對(duì)象,而 Log4jLoggerAdapter 對(duì)外提供的方法,都是通過 log4j 的 Logger 進(jìn)行具體實(shí)現(xiàn)。

總之,slf4j 的 Logger 接口的方法通過 Log4jLoggerAdapter 進(jìn)行包裝和轉(zhuǎn)換,交由 log4j 的 Logger 去執(zhí)行,這就達(dá)到了連接 slf4j-api 和 log4j 的目的。而此時(shí),slf4j-api 不并關(guān)系日志是如何實(shí)現(xiàn)記錄,對(duì)此也無感知。

小結(jié)

本文通過源碼跟蹤,逐步分析了 slf4j 項(xiàng)目中橋接器項(xiàng)目的運(yùn)作機(jī)制,其中還涉及到了 SPI 機(jī)制、版本及依賴檢查小技巧、橋接器運(yùn)作本質(zhì)(適配器模式)等。其實(shí),在 slf4j 項(xiàng)目中還有文中提到的反向橋接器,其實(shí)基本機(jī)制也是如此,感興趣的朋友可以閱讀一下 log4j-over-slf4j 中的源碼。

到此這篇關(guān)于淺談slf4j中的橋接器是如何運(yùn)作的 的文章就介紹到這了,更多相關(guān)slf4j 橋接器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis 一級(jí)緩存與二級(jí)緩存的實(shí)現(xiàn)

    Mybatis 一級(jí)緩存與二級(jí)緩存的實(shí)現(xiàn)

    mybatis作為一個(gè)流行的持久化工具,緩存必然是缺少不了的組件。通過這篇文章,就讓我們來了解一下一級(jí)緩存與二級(jí)緩存的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • Java Hibernate中的多種查詢方式及示例

    Java Hibernate中的多種查詢方式及示例

    Hibernate提供了多種查詢方式,包括HQL查詢、SQL查詢、QBC查詢和Criteria查詢等。HQL查詢是基于面向?qū)ο蟮牟樵冋Z言,可以方便地進(jìn)行對(duì)象導(dǎo)航和多表查詢;SQL查詢?cè)试S用戶直接使用SQL語句進(jìn)行查詢;QBC查詢是基于Criteria的查詢,具有類型安全和可重用性的優(yōu)勢(shì)
    2023-04-04
  • MyBatis持久層框架的用法知識(shí)小結(jié)

    MyBatis持久層框架的用法知識(shí)小結(jié)

    MyBatis 本是apache的一個(gè)開源項(xiàng)目iBatis,接下來通過本文給大家介紹MyBatis持久層框架的用法知識(shí)小結(jié),非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧
    2016-07-07
  • Java訪問修飾符原理及代碼解析

    Java訪問修飾符原理及代碼解析

    這篇文章主要介紹了Java訪問修飾符原理及代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • Java迭代器遍歷list的方法及代碼分析

    Java迭代器遍歷list的方法及代碼分析

    在本篇內(nèi)容里系小編給大家分享的是一篇關(guān)于Java迭代器遍歷list的方法總結(jié)內(nèi)容,有需要的朋友們可以參考學(xué)習(xí)下。
    2022-11-11
  • Java中的定時(shí)器Timer詳解

    Java中的定時(shí)器Timer詳解

    這篇文章主要為大家詳細(xì)介紹了Java定時(shí)器Timer的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 詳解在Spring中如何自動(dòng)創(chuàng)建代理

    詳解在Spring中如何自動(dòng)創(chuàng)建代理

    這篇文章主要介紹了詳解在Spring中如何自動(dòng)創(chuàng)建代理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • 關(guān)于SpringMVC請(qǐng)求域?qū)ο蟮臄?shù)據(jù)共享問題

    關(guān)于SpringMVC請(qǐng)求域?qū)ο蟮臄?shù)據(jù)共享問題

    這篇文章主要介紹了SpringMVC請(qǐng)求域?qū)ο蟮臄?shù)據(jù)共享問題,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • java編程實(shí)現(xiàn)獲取服務(wù)器IP地址及MAC地址的方法

    java編程實(shí)現(xiàn)獲取服務(wù)器IP地址及MAC地址的方法

    這篇文章主要介紹了java編程實(shí)現(xiàn)獲取機(jī)器IP地址及MAC地址的方法,實(shí)例分析了Java分別針對(duì)單網(wǎng)卡及多網(wǎng)卡的情況下獲取服務(wù)器IP地址與MAC地址的相關(guān)技巧,需要的朋友可以參考下
    2015-11-11
  • SpringCloud超詳細(xì)講解微服務(wù)網(wǎng)關(guān)Zuul

    SpringCloud超詳細(xì)講解微服務(wù)網(wǎng)關(guān)Zuul

    這篇文章主要介紹了SpringCloud Zuul微服務(wù)網(wǎng)關(guān),負(fù)載均衡,熔斷和限流,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07

最新評(píng)論

沙坪坝区| 武乡县| 西城区| 浦县| 沐川县| 外汇| 保亭| 会东县| 油尖旺区| 登封市| 册亨县| 合川市| 京山县| 富民县| 毕节市| 石城县| 哈巴河县| 康定县| 江西省| 西乌珠穆沁旗| 大宁县| 济源市| 宕昌县| 洛扎县| 巨鹿县| 丰台区| 岗巴县| 临猗县| 方山县| 邢台市| 玛曲县| 汝城县| 寿光市| 柘荣县| 乌海市| 台安县| 临猗县| 天长市| 鄯善县| 玛沁县| 莱阳市|