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ì)更好±锬妫看圖:
市場(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í)行的流程。
日志工廠類加載日志適配器類過(guò)程江醇。
通過(guò)gotLog
方法觸發(fā)創(chuàng)建日志適配器類對(duì)象和日志組件對(duì)象濒憋。
本文作者:IT-CRUD
原文地址:http://blog.itcrud.com/blogs/2018/09/orm-mybatis-source-logging
版權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)注明出處