mybatis源碼-日志模塊-logging

1.包結(jié)構(gòu)

image.png

2.日志模塊類圖

image.png

3.適配器模式

1.mybatis 沒有本身的日志實現(xiàn),使用的都是比較流行的第三方組件 比如 log4j ,commonlog 等

2.mybatis 實現(xiàn)了自己的log 接口,自己的一套,那么如何和市面上面 流行的日志框架做整合呢?這個時候 就需要用到適配器模式

什么是適配器模式:舉個簡單的例子就是 你有兩孔插頭,墻上只有三孔插座,那么這個時候如何接到電呢?所以你就從淘寶上面買了一個兩孔轉(zhuǎn)三孔的轉(zhuǎn)換器,這個玩意就是適配器。

類適配器:適配器使用繼承,實現(xiàn)接口的方式
image.png
對象適配器:適配器使用私有化對象的方式
image.png
在對象適配器模式結(jié)構(gòu)圖中包含如下幾個角色:

Target(目標抽象類):目標抽象類定義客戶所需接口,可以是一個抽象類或接口,也可以是具體類。

Adapter(適配器類):適配器可以調(diào)用另一個接口,作為一個轉(zhuǎn)換器,對Adaptee和Target進行適配,適配器類是適配器模式的核心,在對象適配器中,它通過繼承Target并關(guān)聯(lián)一個Adaptee對象使二者產(chǎn)生聯(lián)系。

Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經(jīng)存在的接口,這個接口需要適配,適配者類一般是一個具體類,包含了客戶希望使用的業(yè)務(wù)方法,在某些情況下可能沒有適配者類的源代碼

Target = 墻上三孔插座
Adapter = 淘寶購買的轉(zhuǎn)換器
Adaptee = 兩孔轉(zhuǎn)換為三孔的需求

而對于mybatis來說,有自己的一套log接口,那么第三方的日志模塊,都會寫一個類,去適轉(zhuǎn)化

適用場景:當調(diào)用雙方都不太容易修改的時候,為了復用現(xiàn)有組件可以使用適配器模式;在系統(tǒng)中接入第三方組 件的時候經(jīng)常被使用到;
注意:如果系統(tǒng)中存在過多的適配器,會增加系統(tǒng)的復雜性,設(shè)計人員應考慮對系統(tǒng)進行重構(gòu);

4.源碼研究

==org.apache.ibatis.logging.Log==
  • 1.mybatis 的日志級別分為 debug trace error warn 等
  • 2.所有mybatis的日志實現(xiàn)類都需要實現(xiàn)這個接口
/**
 * @author Clinton Begin
 */
public interface Log {

  boolean isDebugEnabled();

  boolean isTraceEnabled();

  void error(String s, Throwable e);

  void error(String s);

  void debug(String s);

  void trace(String s);

  void warn(String s);
}

==org.apache.ibatis.logging.LogFactory== 日志工廠
  • LogFactory 主要用來生產(chǎn),以及實例化日志,判斷使用哪些日志,使用日志優(yōu)先級
  • mybatis 沒有自己本身的日志實現(xiàn)

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public final class LogFactory {

  /**
   * Marker to be used by logging implementations that support markers
   */
  public static final String MARKER = "MYBATIS";

  //第三方日志組件構(gòu)造器,默認為空
  private static Constructor<? extends Log> logConstructor;

  
  //舉例說明:slf4j
  /**
   * 1.類加載器加載LogFactory 當前類 執(zhí)行static 靜態(tài)代碼塊
   * 加載順序:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog        
   * 2.執(zhí)行
   
   * tryImplementation(new Runnable() {
   *   @Override
   *  public void run() {
   *     useSlf4jLogging();
   *  }
   *});  
   
   * 3.先運行useSlf4jLogging();
   * 4.setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
   * 5.執(zhí)行
   *  Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
   *  Log log = candidate.newInstance(LogFactory.class.getName());
   *  Slf4jImpl.class 獲取構(gòu)造器
   *  candidate.newInstance() 調(diào)用 Slf4jImpl構(gòu)造方法進行日志對象實例化
   * 6.mybatis pom.xml里面所有關(guān)于日志的jar包都會寫 <optional>false</optional>,所以 你當前項目依賴mybatis并不會加載相應的日志jar包
   *   當系統(tǒng)里面找不到Slf4j的日志jar包,那么就會報ClassNotFoundException 并且會被tryImplementation 捕獲住,并且ignore 不做任何處理
   *   一層一層往下找,最后找到useNoLogging
   */
  static {
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useSlf4jLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useCommonsLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useLog4J2Logging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useLog4JLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useJdkLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useNoLogging();
      }
    });
  }

  //私有化,不可以自己創(chuàng)建Mybatis 的日志工廠,只能在static靜態(tài)代碼塊初始化
  private LogFactory() {
    // disable construction
  }

  public static Log getLog(Class<?> aClass) {
    return getLog(aClass.getName());
  }

  public static Log getLog(String logger) {
    try {
      return logConstructor.newInstance(logger);
    } catch (Throwable t) {
      throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
    }
  }

  public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
    setImplementation(clazz);
  }

  public static synchronized void useSlf4jLogging() {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }

  public static synchronized void useCommonsLogging() {
    setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
  }

  public static synchronized void useLog4JLogging() {
    setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
  }

  public static synchronized void useLog4J2Logging() {
    setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
  }

  public static synchronized void useJdkLogging() {
    setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
  }

  public static synchronized void useStdOutLogging() {
    setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
  }

  public static synchronized void useNoLogging() {
    setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
  }

  private static void tryImplementation(Runnable runnable) {
    if (logConstructor == null) {
      try {
        runnable.run();
      } catch (Throwable t) {
        // ignore
      }
    }
  }

  //實例化第三方日志組件,調(diào)用構(gòu)造方法
  private static void setImplementation(Class<? extends Log> implClass) {
    try {
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      Log log = candidate.newInstance(LogFactory.class.getName());
      if (log.isDebugEnabled()) {
        log.debug("Logging initialized using '" + implClass + "' adapter.");
      }
      logConstructor = candidate;
    } catch (Throwable t) {
      throw new LogException("Error setting Log implementation.  Cause: " + t, t);
    }
  }

}

==org.apache.ibatis.logging.Log.Log4j2Impl==
1.log4j 實現(xiàn) Log 接口,每個方法使用log4j實現(xiàn)進行適配,其他日志實現(xiàn)類基本都是差不多
2.構(gòu)造方法Log4j2Impl進行實例化log4j


public class Log4j2Impl implements Log {

  private final Log log;

  public Log4j2Impl(String clazz) {
    Logger logger = LogManager.getLogger(clazz);

    if (logger instanceof AbstractLogger) {
      log = new Log4j2AbstractLoggerImpl((AbstractLogger) logger);
    } else {
      log = new Log4j2LoggerImpl(logger);
    }
  }

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

  @Override
  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }

  @Override
  public void error(String s, Throwable e) {
    log.error(s, e);
  }

  @Override
  public void error(String s) {
    log.error(s);
  }

  @Override
  public void debug(String s) {
    log.debug(s);
  }

  @Override
  public void trace(String s) {
    log.trace(s);
  }

  @Override
  public void warn(String s) {
    log.warn(s);
  }

1.類加載器加載LogFactory 當前類 執(zhí)行static 靜態(tài)代碼塊,優(yōu)先級加載順序:slf4J →
commonsLoging → Log4J2 → Log4J → JdkLog
2.執(zhí)行
tryImplementation(new Runnable() {
@Override
public void run() {
useSlf4jLogging();
}
});
3.先運行useSlf4jLogging();
4.setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
5.執(zhí)行
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
Log log = candidate.newInstance(LogFactory.class.getName());
Slf4jImpl.class 獲取構(gòu)造器
candidate.newInstance() 調(diào)用 Slf4jImpl構(gòu)造方法進行日志對象實例化
6.mybatis pom.xml里面所有關(guān)于日志的jar包都會寫 <optional>false</optional> (mybatis源碼是都這些日志框架),所以 你當前項目依賴mybatis并不會加載相應的日志jar包
當系統(tǒng)里面找不到Slf4j的日志jar包,那么就會報ClassNotFoundException 并且會被tryImplementation 捕獲住,并且ignore 不做任何處理
一層一層往下找,最后找到useNoLogging

總結(jié):
1.mybatis 利用maven依賴傳遞的關(guān)系,設(shè)置optional = false ,不會加載任何第三方的日志框架,而是根據(jù)用戶自己加載的日志框架,進行使用。
2.技術(shù)上面使用適配器模式將流行的日志框架轉(zhuǎn)換為自己的日志格式。使用工廠模式生產(chǎn)想要的日志。使用反射實例化對象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容