龙空技术网

Mybatis整合日志框架-工厂设计模式+适配器设计模式

王朋code 127

前言:

眼前兄弟们对“apacheabt”都比较重视,小伙伴们都需要剖析一些“apacheabt”的相关文章。那么小编在网摘上搜集了一些对于“apacheabt””的相关内容,希望咱们能喜欢,同学们快快来了解一下吧!

由于常用的日志框架的对外接口各不相同,Mybatis为了复用和集成这些第三方日志组件,在其日志模块中,提供了多种Adapter,将这些第三方日志组件对外接口适配成org.apache.ibatis.logging.Log,这样Myabtis 就可以通过Log接口调用第三方日志了,接下来我们来看一下具体的代码

关于Mybatis集成日志框架设置

<setting name="logImpl" value="STDOUT_LOGGING"/>

日志属性

对应日志模块包名

实现方式

SLF4J

slf4j

使用SLF4J日志框架实现

LOG4J

log4j

使用Log4J日志框架实现(1.x版本)

LOG4J2

log4j2

使用Log4J日志框架实现(2.x版本)

JDK_LOGGING

jdk14

使用java.util.logging实现

COMMONS_LOGGING

commons

使用Apache Commons Logging实现

STDOUT_LOGGING

stdout

使用System类实现

NO_LOGGING

nologging

不打印日志

在《Mybatis核心源码-通过sqlSession获取映射器代理工厂》提到了XMLConfigBuilder#parseConfiguration中的mapperElement方法,今天我们来看一下settingsElement方法是如何设置日志框架的

private void settingsElement(XNode context) throws Exception {    if (context != null) {      Properties props = context.getChildrenAsProperties();      // Check that all settings are known to the configuration class      //检查下是否在Configuration类里都有相应的setter方法(没有拼写错误)      MetaClass metaConfig = MetaClass.forClass(Configuration.class);      for (Object key : props.keySet()) {        if (!metaConfig.hasSetter(String.valueOf(key))) {          throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");        }      }            //下面非常简单,一个个设置属性      ...      //显式定义用什么log框架,不定义则用默认的自动发现jar包机制      //resolveClass实际是使用的别名查询对应的calss类      configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));    }  }
//假设我们使用的是标准输出STDOUT_LOGGING//org.apache.ibatis.logging.stdout.StdOutImpl.classpublic void setLogImpl(Class<?> logImpl) {    if (logImpl != null) {      this.logImpl = (Class<? extends Log>) logImpl;      LogFactory.useCustomLogging(this.logImpl);    }  }

接下来我们来看LogFactory工厂设计模式:

静态代码块的作用是如果我们没有在配置文件中指定使用那哪个日志框架的话,这里会尝试挨个进行设置,如果获取到了就不会再设置别的了,因为tryImplementation中有判断

假设我们自己配置了日志框架,则会调用setImplementation方法,将logConstructor设置成指定的class类,这里我们使用的是标准输出:org.apache.ibatis.logging.stdout.StdOutImpl.class

当设置完后,使用LogFactory.getLog获取具体日志框架时,就会根据构造方法创建对应的StdOutImpl实例,返回

public final class LogFactory {  //具体究竟用哪个日志框架,那个框架所对应logger的构造函数  private static Constructor<? extends Log> logConstructor;//静态代码块//启动的时候尝试挨个进行设置,如果获取到了就不会在设置别的了  static {    //这边乍一看以为开了几个并行的线程去决定使用哪个具体框架的logging,其实不然    ...    //log4j    tryImplementation(new Runnable() {      @Override      public void run() {        useLog4JLogging();      }    });    //jdk logging    tryImplementation(new Runnable() {      @Override      public void run() {        useJdkLogging();      }    });    //没有日志    tryImplementation(new Runnable() {      @Override      public void run() {        useNoLogging();      }    });  }  //单例模式,不得自己new实例  private LogFactory() {    // disable construction  }  //根据传入的类来构建Log  public static Log getLog(Class<?> aClass) {    return getLog(aClass.getName());  }  //根据传入的类名来构建Log  public static Log getLog(String logger) {    try {      //构造函数,参数必须是一个,为String型,指明logger的名称      return logConstructor.newInstance(new Object[] { logger });    } catch (Throwable t) {      throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);    }  }  //提供一个扩展功能,如果以上log都不满意,可以使用自定义的log  public static synchronized void useCustomLogging(Class<? extends Log> clazz) {    setImplementation(clazz);  }  public static synchronized void useSlf4jLogging() {    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);  }  public static synchronized void useLog4JLogging() {    setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);  }  //这个没用到  public static synchronized void useStdOutLogging() {    setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);  }  public static synchronized void useNoLogging() {    setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);  }//启动尝试设置框架调用的是这里  private static void tryImplementation(Runnable runnable) {    //如果logConstructor有值就不会再设置了    if (logConstructor == null) {      try {      	//这里调用的不是start,而是run!根本就没用多线程嘛!        runnable.run();      } catch (Throwable t) {        // ignore      }    }  }//自定义日志输出框架实际调用的是这里  private static void setImplementation(Class<? extends Log> implClass) {    try {      Constructor<? extends Log> candidate = implClass.getConstructor(new Class[] { String.class });      Log log = candidate.newInstance(new Object[] { LogFactory.class.getName() });      log.debug("Logging initialized using '" + implClass + "' adapter.");      //设置logConstructor,一旦设上,表明找到相应的log的jar包了,那后面别的log就不找了。      logConstructor = candidate;    } catch (Throwable t) {      throw new LogException("Error setting Log implementation.  Cause: " + t, t);    }  }}

我们再来看StdOutImpl类,看到所有的标准输出,就是使用了java的System.out.println(s)方法打印了日志

public class StdOutImpl implements Log {  public StdOutImpl(String clazz) {    // Do Nothing  }  @Override  public boolean isDebugEnabled() {    return true;  }  @Override  public boolean isTraceEnabled() {    return true;  }  @Override  public void error(String s, Throwable e) {    System.err.println(s);    e.printStackTrace(System.err);  }  @Override  public void error(String s) {    System.err.println(s);  }  @Override  public void debug(String s) {    System.out.println(s);  }  @Override  public void trace(String s) {    System.out.println(s);  }  @Override  public void warn(String s) {    System.out.println(s);  }}

我们来看适配器设计模式的应用:Mybatis中的log4j日志框架

假设我们设置的是LOG4J,则LogFactory会返回Log4jImpl类,而Log4jImpl中使用了适配器设计模式,在Log4jImpl的构造函数中初始化了log4j的日志对象,当我们在使用log.error时,实际使用的是log4j日志框架的log方法

public class Log4jImpl implements Log {    private static final String FQCN = Log4jImpl.class.getName();//这里设置了log4j的Logger,然后在Log4jImpl的构造函数中初始化了log4j  private 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);  }}

标签: #apacheabt