溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

MyBatis源碼分析之日志logging的示例分析

發(fā)布時間:2021-08-30 11:45:13 來源:億速云 閱讀:153 作者:小新 欄目:編程語言

這篇文章將為大家詳細(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)系如下:

MyBatis源碼分析之日志logging的示例分析

除了 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é)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(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)容。

AI