ORM框架之Mybatis(六):logging源碼實(shí)現(xiàn)分析

mybatis框架源碼的實(shí)現(xiàn)相對(duì)spring來(lái)說(shuō)要簡(jiǎn)單的很多呢堰,模塊的分工也很明確机久,每個(gè)模塊的代碼量也不是很大,比較容易閱讀藏鹊,如果你對(duì)設(shè)計(jì)模式很了解的話润讥。里面用到很多設(shè)計(jì)模式,如工廠模式盘寡、代理模式楚殿、裝飾器模式、適配器模式等等竿痰,都是值得平時(shí)開發(fā)學(xué)習(xí)和借鑒的脆粥,都說(shuō)高手的代碼都是向高級(jí)框架靠攏砌溺,誰(shuí)知道他是自己設(shè)計(jì)還是看了源碼學(xué)習(xí)的呢,對(duì)不变隔?

mybatis的模塊比較多抚吠,如對(duì)外的用SqlSession、內(nèi)部底層日志logging弟胀、數(shù)據(jù)源模塊、反射等喊式,這一篇來(lái)看mybatis源碼中最簡(jiǎn)單的部分logging日志孵户。這個(gè)模塊主要就是用工廠模式生成適配不同logging組件的日志類。然后就是適配器模式岔留,適配不同的logging組件夏哭,并支持無(wú)侵入的logging組件擴(kuò)展。

logging整體了解

想了解細(xì)節(jié)献联,先了解整體竖配,會(huì)更好±锬妫看圖:

image

市場(chǎng)上日志組件有很多进胯,各個(gè)公司采用的日志組件不盡相同,但是可能都要使用到mybatis原押,如果mybatis只能支持一種日志的話胁镐,那么就會(huì)出現(xiàn)其他日志組件無(wú)法打印日志的問(wèn)題。

此時(shí)使用適配器模式诸衔,不論其他的組件是什么樣的盯漂,統(tǒng)一使用適配類去對(duì)這些組件進(jìn)行封裝,適配類都實(shí)現(xiàn)Log接口笨农,將Log暴露給mybatis調(diào)用方就缆,調(diào)用方直接調(diào)用log接口內(nèi)的方法即可。不用管底層到底是適配哪個(gè)日志組件谒亦。另外每種日志組件的日志級(jí)別分類都是有所差別的竭宰,做了統(tǒng)一封裝,就不用考慮日志級(jí)別變化的問(wèn)題诊霹。

這種設(shè)計(jì)拓展性很好羞延,如果需要添加日志組件,只要寫一個(gè)適配類去封裝此日志組件脾还,然后在工廠類中添加日志適配類加載的代碼就可以伴箩,上層的業(yè)務(wù)代碼是無(wú)需任何修改,是無(wú)感的鄙漏。

看一下源碼

Log接口類

Log接口類的代碼:

public interface Log {
  boolean isDebugEnabled();//是否可以debug日志
  boolean isTraceEnabled();//是否可以跟進(jìn)日志
  //錯(cuò)誤
  void error(String s, Throwable e);
  //錯(cuò)誤
  void error(String s);
  //調(diào)試
  void debug(String s);
  //跟進(jìn)
  void trace(String s);
  //警告
  void warn(String s);
}

這里日志接口很簡(jiǎn)單嗤谚,就四種日志級(jí)別棺蛛。如果多l(xiāng)og4j、commons-logging這些日志組件有所了解的話巩步,可以知道他們的日志級(jí)別都是有所不同旁赊。因此這里就必須要是做適配了,適配器就上場(chǎng)嘍椅野。(slf4j為例)

Slf4jImpl實(shí)現(xiàn)類
public class Slf4jImpl implements Log {

  private Log log;
  // 構(gòu)造函數(shù)(很重要)
  public Slf4jImpl(String clazz) {
    // 獲取logger實(shí)例终畅,這是對(duì)應(yīng)到組件的logger
    Logger logger = LoggerFactory.getLogger(clazz);

    if (logger instanceof LocationAwareLogger) {
      try {
        // check for slf4j >= 1.6 method signature
        logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
        // 創(chuàng)建適配對(duì)象
        log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
        return;
      } catch (SecurityException e) {
        // fail-back to Slf4jLoggerImpl
      } catch (NoSuchMethodException e) {
        // fail-back to Slf4jLoggerImpl
      }
    }

    // Logger is not LocationAwareLogger or slf4j version < 1.6
    log = new Slf4jLoggerImpl(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);
  }
}

這里主要看的就是構(gòu)造函數(shù),看構(gòu)造函數(shù)是如何創(chuàng)建適配器對(duì)象的竟闪。先根據(jù)實(shí)際日志組件類創(chuàng)建logger實(shí)例离福,然后根據(jù)對(duì)logger實(shí)例進(jìn)行包裝,得到一個(gè)適配后的log實(shí)例炼蛤,也就是對(duì)應(yīng)日志組件的log適配器對(duì)象妖爷。Slf4jLocationAwareLoggerImpl內(nèi)的代碼就不跟進(jìn)去看了,里面很簡(jiǎn)單理朋,就是將傳入的logger賦值給成員變量絮识。然后在四種日志級(jí)別的方法里面都使用這個(gè)logger對(duì)象進(jìn)行日志打印。

LogFactory工廠類

工廠類做了兩件事情嗽上,第一步在靜態(tài)代碼塊中依次掃描日志實(shí)現(xiàn)次舌,然后根據(jù)優(yōu)先級(jí)加載日志實(shí)現(xiàn)類的構(gòu)造器,只會(huì)加載一個(gè)兽愤,誰(shuí)在前加載誰(shuí)垃它。第二步提供getLog方法,通過(guò)此方法中調(diào)用構(gòu)造器的newInstance方法構(gòu)造正式的日志對(duì)象和日志適配對(duì)象烹看。

//加載日志構(gòu)造器的靜態(tài)代碼塊
//根據(jù)順序依次執(zhí)行日志類的加載国拇,有加載順序和優(yōu)先級(jí)
//順序(優(yōu)先級(jí)):slf4j->commons-logging->log4j-logging->log4j->jdk->no-loging
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();
    }
  });
}

詳細(xì)如代碼,但是這里要知道的是惯殊,加載的日志構(gòu)造器并不是對(duì)應(yīng)日志組件的日志類構(gòu)造器酱吝,而是適配器的構(gòu)造器⊥了迹可以看一下上面適配器的源碼構(gòu)造方法务热。

//獲取日志對(duì)象,調(diào)用getLog(String logger)方法
public static Log getLog(Class<?> aClass) {
  return getLog(aClass.getName());
}

//獲取日志對(duì)象
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);
  }
}

創(chuàng)建日志實(shí)例的方法如上己儒,很簡(jiǎn)單崎岂,就是newInstance方法的調(diào)用。

Overview

日志組件適配的整個(gè)過(guò)程不難闪湾,代碼也很簡(jiǎn)潔冲甘,過(guò)程也很清晰,然后現(xiàn)在通過(guò)下面的圖總結(jié)一下執(zhí)行的流程。

mybatis日志源碼-加載

日志工廠類加載日志適配器類過(guò)程江醇。

mybatis日志源碼-創(chuàng)建

通過(guò)gotLog方法觸發(fā)創(chuàng)建日志適配器類對(duì)象和日志組件對(duì)象濒憋。

微信公眾號(hào)

本文作者:IT-CRUD
原文地址:http://blog.itcrud.com/blogs/2018/09/orm-mybatis-source-logging
版權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末陶夜,一起剝皮案震驚了整個(gè)濱河市凛驮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌条辟,老刑警劉巖黔夭,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異羽嫡,居然都是意外死亡纠修,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門厂僧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人了牛,你說(shuō)我怎么就攤上這事颜屠。” “怎么了鹰祸?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵甫窟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蛙婴,道長(zhǎng)粗井,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任街图,我火速辦了婚禮浇衬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘餐济。我一直安慰自己耘擂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布絮姆。 她就那樣靜靜地躺著醉冤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篙悯。 梳的紋絲不亂的頭發(fā)上蚁阳,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音鸽照,去河邊找鬼螺捐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的归粉。 我是一名探鬼主播椿疗,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼糠悼!你這毒婦竟也來(lái)了届榄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤倔喂,失蹤者是張志新(化名)和其女友劉穎铝条,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體席噩,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡班缰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悼枢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片埠忘。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖馒索,靈堂內(nèi)的尸體忽然破棺而出莹妒,到底是詐尸還是另有隱情,我是刑警寧澤绰上,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布旨怠,位于F島的核電站,受9級(jí)特大地震影響蜈块,放射性物質(zhì)發(fā)生泄漏鉴腻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一百揭、第九天 我趴在偏房一處隱蔽的房頂上張望爽哎。 院中可真熱鬧,春花似錦器一、人聲如沸阅虫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至猜敢,卻和暖如春缝彬,著一層夾襖步出監(jiān)牢的瞬間生棍,已是汗流浹背寺鸥。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留获印,地道東北人述雾。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親玻孟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唆缴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

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