MyBatis日志模块分析

非 Y 不嫁゛ 提交于 2019-11-29 08:53:52

MyBatis日志模块分析

标签(空格分隔): 未分类


日志功能代码所在包

org.apache.ibatis.logging

日志模块的加载顺序

mybatis 没有自己的日志模块 他是使用的第三方日志(也有jdk自带日志) 日志的加载顺序 slf4J → commonsLoging → Log4J2 → Log4J → JdkLog

相关代码

在静态代码块儿中声明加载顺序

public final class LogFactory {
  //被选定的第三方日志组件适配器的构造方法
  private static Constructor<? extends Log> logConstructor;

  /**
   * Marker to be used by logging implementations that support markers
   */
  public static final String MARKER = "MYBATIS";

  //被选定的第三方日志组件适配器的构造方法
  private static Constructor<? extends Log> logConstructor;

  //自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog
  static {
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

这里用到了java8的语法糖 tryImplementation方法需要一个runnable类型的参数,runnable被打上了@FunctionalInterface注解,所以可以使用lambda语法简写 logConstructor 保存着日志对象的构造方法,之后通过反射创建对象,只有logConstructor == null的时候采取调用running的run方法,那么run的内容是什么呢?

tryImplementation(LogFactory::useSlf4jLogging);

private static void tryImplementation(Runnable runnable) {
    if (logConstructor == null) {//当构造方法不为空才执行方法
      try {
        runnable.run();
      } catch (Throwable t) {
        // ignore
      }
    }
  }

LogFactory::useSlf4jLogging setImplementation方法尝试通过传递过来的Class创建类(通过反射拿到构造方法),如果成功,讲构造方法保存到logConstructor中备用

public static synchronized void useSlf4jLogging() {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }
  
  
  //通过指定的log类来初始化构造方法
  private static void setImplementation(Class<? extends Log> implClass) {
    try {
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      Log log = candidate.newInstance(LogFactory.class.getName());
      if (log.isDebugEnabled()) {
        log.debug("Logging initialized using '" + implClass + "' adapter.");
      }
      logConstructor = candidate;
    } catch (Throwable t) {
      throw new LogException("Error setting Log implementation.  Cause: " + t, t);
    }
  }

这用到的设计模式 适配器模式 因为不同的日志组件对日志的info error等定义不同,mybatis把他们统一成如下级别

package org.apache.ibatis.logging;
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);

我们看优先级最高的日志slf4J的实现类

package org.apache.ibatis.logging.log4j;

import org.apache.ibatis.logging.Log;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * @author Eduardo Macarron
 */
public class Log4jImpl implements Log {
  private static final String FQCN = Log4jImpl.class.getName();
  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);
  }
}

日志功能的体现

MyBatis通过动态代理的方式增强了Connection,PreparedStatement,ResultSet.其对应的实现是xxxLogger类

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!