Java开发中经常用到的日志框架有很多,Log4j、Log4j2、slf4j等等,Mybatis定义了一套统一的日志接口供上层使用,并为上述常用的日志框架提供了相应的适配器。有关适配器模式例子可以参考 设计模式整理 。
适配器模式中有接口适配和委托适配两种,当然我们可以把他们统一成又有接口又有委托,而不使用抽象类。
首先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日志工厂使用这些适配器,从它的静态代码块来看,是启用线程任务来尝试加载各种日志框架。
// // 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(); } }); } }