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(目標(biāo)抽象類):目標(biāo)抽象類定義客戶所需接口胜卤,可以是一個抽象類或接口,也可以是具體類赁项。

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)化

適用場景:當(dāng)調(diào)用雙方都不太容易修改的時候材彪,為了復(fù)用現(xiàn)有組件可以使用適配器模式观挎;在系統(tǒng)中接入第三方組 件的時候經(jīng)常被使用到;
注意:如果系統(tǒng)中存在過多的適配器段化,會增加系統(tǒng)的復(fù)雜性嘁捷,設(shè)計人員應(yīng)考慮對系統(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)造器喘蟆,默認(rèn)為空
  private static Constructor<? extends Log> logConstructor;

  
  //舉例說明:slf4j
  /**
   * 1.類加載器加載LogFactory 當(dāng)前類 執(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>缓升,所以 你當(dāng)前項目依賴mybatis并不會加載相應(yīng)的日志jar包
   *   當(dāng)系統(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 當(dāng)前類 執(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源碼是都這些日志框架),所以 你當(dāng)前項目依賴mybatis并不會加載相應(yīng)的日志jar包
當(dāng)系統(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)系作者
  • 序言:七十年代末僧诚,一起剝皮案震驚了整個濱河市遮婶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌湖笨,老刑警劉巖旗扑,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異慈省,居然都是意外死亡臀防,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袱衷,“玉大人捎废,你說我怎么就攤上這事≈略铮” “怎么了登疗?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嫌蚤。 經(jīng)常有香客問我辐益,道長,這世上最難降的妖魔是什么脱吱? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任智政,我火速辦了婚禮,結(jié)果婚禮上急凰,老公的妹妹穿的比我還像新娘女仰。我一直安慰自己,他們只是感情好抡锈,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布疾忍。 她就那樣靜靜地躺著,像睡著了一般床三。 火紅的嫁衣襯著肌膚如雪一罩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天撇簿,我揣著相機與錄音聂渊,去河邊找鬼。 笑死四瘫,一個胖子當(dāng)著我的面吹牛汉嗽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播找蜜,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼饼暑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了洗做?” 一聲冷哼從身側(cè)響起弓叛,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诚纸,沒想到半個月后撰筷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡畦徘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年毕籽,在試婚紗的時候發(fā)現(xiàn)自己被綠了抬闯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡影钉,死狀恐怖画髓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情平委,我是刑警寧澤奈虾,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站廉赔,受9級特大地震影響肉微,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜡塌,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一碉纳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧馏艾,春花似錦劳曹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至房资,卻和暖如春蜕劝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轰异。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工岖沛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搭独。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓婴削,卻偏偏與公主長得像,于是被迫代替她去往敵國和親牙肝。 傳聞我的和親對象是個殘疾皇子馆蠕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容