面试官:mybatis是如何适配日志的

mybatis基本支持所有的日志框架,slf4j,commons-logging,log4j2,log4j,jdklogging。支持这么多日志框架,mybatis是如何适配的呢?本文将告诉你答案。

mybatis定义了自己的Log接口,该接口就是mybatis的日志标准:

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

使用LogFactory来获取Log对象,可以看到在LogFactory中,静态代码块依次尝试初始化了所有的日志框架,如果有目标日志实现类则设置对应的日志实现,否则继续向下尝试,忽略异常。

static {
    
    
  tryImplementation(LogFactory::useSlf4jLogging);
  tryImplementation(LogFactory::useCommonsLogging);
  tryImplementation(LogFactory::useLog4J2Logging);
  tryImplementation(LogFactory::useLog4JLogging);
  tryImplementation(LogFactory::useJdkLogging);
  tryImplementation(LogFactory::useNoLogging);
}
private static void tryImplementation(Runnable runnable) {
    
    
  if (logConstructor == null) {
    
    
    try {
    
    
      runnable.run();
    } catch (Throwable t) {
    
    
      // ignore
    }
  }
}

那如果所有的日志框架实现都没有找到呢?mybatis这里给出了保底方案,就是上面代码中最后一个尝试设置的LogFactory::useNoLogging不打印日志。

LogFactory中有一个构造器成员变量,用来记录最终的日志实现,注意这里所说的实现是经过mybatis包装了一层的日志实现框架(适配器)。

private static Constructor<? extends Log> logConstructor;
public static Log getLog(String logger) {
    
    
  try {
    
    
    return logConstructor.newInstance(logger);
  } catch (Throwable t) {
    
    
    throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
  }
}

有了适配器,在每种日志框架的适配器中就可以使用目标日志框架了,我们以log4j为例,看一下适配器结构。可以看到适配器中持有了真正的log4j引用,之后的代码就和使用log4j没什么区别了。

package org.apache.ibatis.logging.log4j;

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

public class Log4jImpl implements Log {
    
    
  private final Logger log;
  public Log4jImpl(String clazz) {
    
    
    log = Logger.getLogger(clazz);
}

既然初始化已经尝试加载实现日志框架了,那我们如何指定某一个日志框架。答案就是mybatis配置文件,我们可以在配置文件中settings节点指定日志实现方式。

<settings>
    <setting name="logImpl" value="LOG4J2"/>
</settings>

这个缩略值LOG4J2是哪来的?在mybatis的配置类Configuration中,构造方法中已经为我们约定好了这一切。

typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

这样,mybatis就可以在初始化完成后,再来一次覆盖设值,所以配置文件中的日志设置具有最高的优先级。

private void loadCustomLogImpl(Properties props) {
    
    
  Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
  configuration.setLogImpl(logImpl);
}

本文我们从全局的角度分析了mybatis日志的适配过程,还有许多细节并未带出,感兴趣的童鞋可以自己跟踪下源码,大有裨益。

觉得有用,欢迎关注。同名公众号【码农小麦】
在这里插入图片描述

扫描二维码关注公众号,回复: 12926266 查看本文章

猜你喜欢

转载自blog.csdn.net/weixin_43275277/article/details/115227981