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();
}
});
}
}