Java開發中常常用到的日誌框架有不少,Log4j、Log4j二、slf4j等等,Mybatis定義了一套統一的日誌接口供上層使用,併爲上述經常使用的日誌框架提供了相應的適配器。有關適配器模式例子能夠參考 設計模式整理 。java
適配器模式中有接口適配和委託適配兩種,固然咱們能夠把他們統一成又有接口又有委託,而不使用抽象類。apache
首先Mybatis中有一個日誌框架的接口,這個接口可能表明以上的各類日誌框架。設計模式
package org.apache.ibatis.logging; public interface Log { boolean isDebugEnabled(); boolean isTraceEnabled(); void error(String var1, Throwable var2); void error(String var1); void debug(String var1); void trace(String var1); void warn(String var1); }
以Slf4j爲例來看它的適配器,這是一個典型的委託適配器,委託了Slf4j自己的對象,以及對該對象的調用。架構
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.ibatis.logging.slf4j; import org.apache.ibatis.logging.Log; import org.slf4j.Logger; class Slf4jLoggerImpl implements Log { private final Logger log; public Slf4jLoggerImpl(Logger logger) { this.log = logger; } public boolean isDebugEnabled() { return this.log.isDebugEnabled(); } public boolean isTraceEnabled() { return this.log.isTraceEnabled(); } public void error(String s, Throwable e) { this.log.error(s, e); } public void error(String s) { this.log.error(s); } public void debug(String s) { this.log.debug(s); } public void trace(String s) { this.log.trace(s); } public void warn(String s) { this.log.warn(s); } }
調用以上適配器,並初始化一個真正的Slf4j的實例來使用該適配器。能夠當作是以上適配器的一個延伸,主要是爲了轉化成一個String類型的單參構造器,方便統一調用。框架
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.ibatis.logging.slf4j; import org.apache.ibatis.logging.Log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.spi.LocationAwareLogger; public class Slf4jImpl implements Log { private Log log; public Slf4jImpl(String clazz) { Logger logger = LoggerFactory.getLogger(clazz); if(logger instanceof LocationAwareLogger) { try { logger.getClass().getMethod("log", new Class[]{Marker.class, String.class, Integer.TYPE, String.class, Object[].class, Throwable.class}); this.log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger)logger); return; } catch (SecurityException var4) { ; } catch (NoSuchMethodException var5) { ; } } this.log = new Slf4jLoggerImpl(logger); } public boolean isDebugEnabled() { return this.log.isDebugEnabled(); } public boolean isTraceEnabled() { return this.log.isTraceEnabled(); } public void error(String s, Throwable e) { this.log.error(s, e); } public void error(String s) { this.log.error(s); } public void debug(String s) { this.log.debug(s); } public void trace(String s) { this.log.trace(s); } public void warn(String s) { this.log.warn(s); } }
Mybatis日誌工廠使用這些適配器,從它的靜態代碼塊來看,是啓用線程任務來嘗試加載各類日誌框架。this
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.ibatis.logging; import java.lang.reflect.Constructor; import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl; import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl; import org.apache.ibatis.logging.log4j.Log4jImpl; import org.apache.ibatis.logging.log4j2.Log4j2Impl; import org.apache.ibatis.logging.nologging.NoLoggingImpl; import org.apache.ibatis.logging.slf4j.Slf4jImpl; import org.apache.ibatis.logging.stdout.StdOutImpl; public final class LogFactory { public static final String MARKER = "MYBATIS"; //日誌框架接口的構造器 private static Constructor<? extends Log> logConstructor; //日誌工廠的私有構造器,即它不能被實例化 private LogFactory() { } public static Log getLog(Class<?> aClass) { return getLog(aClass.getName()); } public static Log getLog(String logger) { try { return (Log)logConstructor.newInstance(new Object[]{logger}); } catch (Throwable var2) { throw new LogException("Error creating logger for logger " + logger + ". Cause: " + var2, var2); } } public static synchronized void useCustomLogging(Class<? extends Log> clazz) { setImplementation(clazz); } public static synchronized void useSlf4jLogging() { setImplementation(Slf4jImpl.class); } public static synchronized void useCommonsLogging() { setImplementation(JakartaCommonsLoggingImpl.class); } public static synchronized void useLog4JLogging() { setImplementation(Log4jImpl.class); } public static synchronized void useLog4J2Logging() { setImplementation(Log4j2Impl.class); } public static synchronized void useJdkLogging() { setImplementation(Jdk14LoggingImpl.class); } public static synchronized void useStdOutLogging() { setImplementation(StdOutImpl.class); } public static synchronized void useNoLogging() { setImplementation(NoLoggingImpl.class); } //若是前面的日誌框架沒有被加載,則加載後面的 private static void tryImplementation(Runnable runnable) { if(logConstructor == null) { try { runnable.run(); } catch (Throwable var2) { ; } } } //實例化適配器,並記錄該適配器的構造器是否爲空 private static void setImplementation(Class<? extends Log> implClass) { try { //獲取指定適配器的構造方法,由於這些適配器的構造器都是一個String類型的單參構造器 Constructor<? extends Log> candidate = implClass.getConstructor(new Class[]{String.class}); //使用構造器來實例化指定的適配器 Log log = (Log)candidate.newInstance(new Object[]{LogFactory.class.getName()}); if(log.isDebugEnabled()) { log.debug("Logging initialized using '" + implClass + "' adapter."); } //初始化日誌工廠的日誌框架構造器字段 logConstructor = candidate; } catch (Throwable var3) { throw new LogException("Error setting Log implementation. Cause: " + var3, var3); } } //根據各類日誌框架,來嘗試加載,依次順序爲Slf4j,Commons,Log4j2,Log4j,jdk,useNo。直到加載成功一個爲止,後面的將再也不加載。 static { tryImplementation(new Runnable() { public void run() { LogFactory.useSlf4jLogging(); } }); tryImplementation(new Runnable() { public void run() { LogFactory.useCommonsLogging(); } }); tryImplementation(new Runnable() { public void run() { LogFactory.useLog4J2Logging(); } }); tryImplementation(new Runnable() { public void run() { LogFactory.useLog4JLogging(); } }); tryImplementation(new Runnable() { public void run() { LogFactory.useJdkLogging(); } }); tryImplementation(new Runnable() { public void run() { LogFactory.useNoLogging(); } }); } }