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