1.包結(jié)構(gòu)
2.日志模塊類圖
3.適配器模式
1.mybatis 沒有本身的日志實現(xiàn)翔脱,使用的都是比較流行的第三方組件 比如 log4j 支鸡,commonlog 等
2.mybatis 實現(xiàn)了自己的log 接口遵岩,自己的一套意敛,那么如何和市面上面 流行的日志框架做整合呢伏蚊?這個時候 就需要用到適配器模式
什么是適配器模式:舉個簡單的例子就是 你有兩孔插頭立轧,墻上只有三孔插座,那么這個時候如何接到電呢躏吊?所以你就從淘寶上面買了一個兩孔轉(zhuǎn)三孔的轉(zhuǎn)換器氛改,這個玩意就是適配器。
類適配器:適配器使用繼承比伏,實現(xiàn)接口的方式
對象適配器:適配器使用私有化對象的方式
在對象適配器模式結(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)想要的日志真朗。使用反射實例化對象。