您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)MyBatis源碼分析之日志logging的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
logging 配置加載
我們先從日志的配置加載開始閱讀, MyBatis 的各項配置的加載過程都可以從 XMLConfigBuilder 類中找到,我們定位到該類下的日志加載方法 loadCustomLogImpl:
private void loadCustomLogImpl(Properties props) { // 從 MyBatis 的 TypeAliasRegistry 中查找 logImpl 鍵所對應(yīng)值的類對象 // 這里 logImpl 對應(yīng)的 value 值可以從 org.apache.ibatis.session.Configuration 的構(gòu)造方法中找到 // 注意 Log 類,這是 MyBatis 內(nèi)部對日志對象的抽象 Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl")); // 將查找到的 Class 對象設(shè)置到 Configuration 對象中 configuration.setLogImpl(logImpl); }
很簡單的一個方法,每行都有注釋,其中 configuration.setLogImpl()
里面調(diào)用了 LogFactory.useCustomLogging()
,這出現(xiàn)了新類 LogFactory 類,接下來我們就來聊聊這個類。
LogFactory
useCustomLogging()方法
LogFactory 是框架內(nèi)部獲取 Log 對象的手段,通過它的名字也能看出來。它有如下幾類方法:
// 注意這個類型的方法都是同步方法 public synchronized static useXxxLogging(...); public static Log getLog(...); private static tryImplementation(Runnable); private static setImplementation(Class);
剛剛我們看到被調(diào)用的方法 useCustomLogging()
方法,是用來設(shè)置內(nèi)部使用的日志框架, MyBatis 自身已經(jīng)適配了一些常見的日志框架,如 Slf4j 、 Commons Log 、 Log4j 等等。
useCustomLogging()
方法內(nèi)部調(diào)用 setImplementation(Class)
方法,此方法代碼如下,功能簡單:
private static void setImplementation(Class<? extends Log> implClass) { try { // 獲取 Log實現(xiàn)類的構(gòu)造方法,它只有一個字符串作為參數(shù) Constructor<? extends Log> candidate = implClass.getConstructor(String.class); // 創(chuàng)建一個 Log 對象,打印 debug 日志 Log log = candidate.newInstance(LogFactory.class.getName()); if (log.isDebugEnabled()) { log.debug("Logging initialized using '" + implClass + "' adapter."); } // ... // 把 candidate 對象設(shè)置到 LogFactory 的靜態(tài)變量 logConstructor,這個靜態(tài)變量在 getLog() 方法 // 中被用到 logConstructor = candidate; } catch (Throwable t) { throw new LogException("Error setting Log implementation. Cause: " + t, t); } }
Log 接口
剛剛我們接觸到了 Log 這個類,它是一個接口,是 MyBatis 內(nèi)部使用的日志對象的抽象,它是為了兼容市面上各種各樣的日志框架,使用了適配器模式,通過 Log 接口來連接 MyBatis 和其他日志框架,通過實現(xiàn) Log 接口連著 MyBatis 和需要適配的日志框架。
Log 接口代碼如下,先試著發(fā)現(xiàn)該接口與其他常見的日志接口的區(qū)別:
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); }
可有發(fā)現(xiàn)?Log 接口缺少了 info 級別的日志輸出方法,個人猜測應(yīng)該是 MyBatis 內(nèi)部不需要 info 級別的日志輸出,畢竟 Log 接口設(shè)計之初就是為了內(nèi)部使用,而框架使用者是不會采用 MyBatis 的日志作為系統(tǒng)的日志。注意一點: 實現(xiàn)了 Log 接口的類必須擁有一個參數(shù)只有一個字符串的構(gòu)造方法 ,MyBatis 就是通過這個構(gòu)造方法創(chuàng)建日志對象的。
MyBatis 適配了許多常見的日志框架,這里就單單介紹 Log4jImpl 類,它代碼非常簡單:
public class Log4jImpl implements Log { private static final String FQCN = Log4jImpl.class.getName(); // 這里包裝了 Log4j 框架的日志對象,從而實現(xiàn)適配 private final Logger log; public Log4jImpl(String clazz) { log = Logger.getLogger(clazz); } @Override public boolean isDebugEnabled() { return log.isDebugEnabled(); } @Override public boolean isTraceEnabled() { return log.isTraceEnabled(); } @Override public void error(String s, Throwable e) { log.log(FQCN, Level.ERROR, s, e); } @Override public void error(String s) { log.log(FQCN, Level.ERROR, s, null); } @Override public void debug(String s) { log.log(FQCN, Level.DEBUG, s, null); } @Override public void trace(String s) { log.log(FQCN, Level.TRACE, s, null); } @Override public void warn(String s) { log.log(FQCN, Level.WARN, s, null); } }
tryImplementation() 方法
LogFactory 類再介紹一下被靜態(tài)代碼塊使用的方法 tryImplementation(Runnable)
。靜態(tài)代碼塊代碼如下:
static { // 依次執(zhí)行如下代碼,當(dāng)沒有該類會拋 ClassNotFoundException ,然后繼續(xù)執(zhí)行 tryImplementation(LogFactory::useSlf4jLogging); tryImplementation(LogFactory::useCommonsLogging); tryImplementation(LogFactory::useLog4J2Logging); tryImplementation(LogFactory::useLog4JLogging); tryImplementation(LogFactory::useJdkLogging); tryImplementation(LogFactory::useNoLogging); }
這個方法有點迷惑性,因為它使用 Runnable 接口作為參數(shù),而 useXxxLOgging()
方法又是同步方法,很容易聯(lián)想到多線程,實際上這里并沒有, Runnable 接口不結(jié)合 Thread 類使用它就是一個普通的函數(shù)接口。除去這些就沒什么了,不過是調(diào)用了 Runnable 的 run()
方法而已。
getLog() 方法
getLog()
沒什么多說的,就是通過反射創(chuàng)建 Log 接口實現(xiàn)類,這里沒有使用到緩存,每次調(diào)用都是創(chuàng)建一個新的對象。
jdbc 包
這個包與其他包有些不同,它的職能是為各個階段的流程提供日志打印,該包一共就五個類,它們的關(guān)系如下:
除了 BaseJdbcLogger 類其他類都實現(xiàn)了 InvocationHandler 接口,這個接口是 JDK 提供的動態(tài)代理接口,所以顯而易見可以知道它們就是通過代理在各個階段打印相應(yīng)的日志。
以下為 BaseJdbcLogger 類的部分說明:
SET_METHODS:靜態(tài)字段,記錄 PreparedStatement 中 set 開頭的的方法名
EXECUTE_METHODS:靜態(tài)字段,記錄 SQL 執(zhí)行的方法名
columnXxx:實例字段,記錄 SQL 參數(shù)信息
statementLog:日志對象
queryStack:查詢棧數(shù)
getParameterValueString():將 SQL 參數(shù)轉(zhuǎn)為一個字符串
removeBreakingWhitespace():移除 SQL 中多余的空白字符
prefix():獲取前綴 ==>/<==
而其余四個類都是簡單的邏輯:判斷執(zhí)行的方法是否為指定方法,然后打印相應(yīng)的日志。
關(guān)于“MyBatis源碼分析之日志logging的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。