Mybatis日志适配学习

一. 指定Mybatis日志框架
1.在mybatis-config.xml中配置如下

<configuration>
  <settings>
    ...
    <setting name="logImpl" value="LOG4J"/>
    ...
  </settings>
</configuration>

2.mybatis启动时会解析xml,通过XMLConfigBuilder中parseConfiguration->loadCustomLogImpl->resolveClass->
configuration.setLogImpl

1.//解析配置文件
private void parseConfiguration(XNode root) {
        try {
            省略
             // 处理<settings>标签
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            
            // 处理Log
            loadCustomLogImpl(settings);
            省略
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
}
2.执行loadCustomLogImpl
private void loadCustomLogImpl(Properties props) {
        //调用父类中resolveClass方法
        Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
        configuration.setLogImpl(logImpl);
}

3. //根据别名解析class
protected <T> Class<? extends T> resolveClass(String alias) {
        if (alias == null) {
            return null;
        }
        try {
            return resolveAlias(alias);
        } catch (Exception e) {
            throw new BuilderException("Error resolving class. Cause: " + e, e);
        }
    }
    
4. //从typeAliasRegistry获取Class
protected <T> Class<? extends T> resolveAlias(String alias) {
        return typeAliasRegistry.resolveAlias(alias);
}

5 //而typeAliasRegistry是在Configuration类 new对象时执行
public 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);
        省略
    }

//此处使用LogFactory设置
public void setLogImpl(Class<? extends Log> logImpl) {
        if (logImpl != null) {
            this.logImpl = logImpl;
            //使用用户指定日志适配器
            LogFactory.useCustomLogging(this.logImpl);
        }
    }

3.关于LogFactory类
日志工厂静态初始化时默认加载日志适配器,根据适配器所需要对应的日志框架Class 如果对应的Class存在则加载一个日志适配器,上面的LogFactory.useCustomLogging(this.logImpl); 使用用户指定的日志适配器替换默认加载的适配器


// 记录当前使用的第三方日志库适配器的构造方法
private static Constructor<? extends Log> logConstructor;

//静态初始化
static {
        // 尝试按照Slf4j、Common Loggin、Log4j2、Log4j、Jdk Logging、No Logging的顺序,
        // 依次加载对应的适配器,一旦加载成功,就会记录到logConstructor字段中,并会停止后续适配器
        tryImplementation(LogFactory::useSlf4jLogging);
        tryImplementation(LogFactory::useCommonsLogging);
        tryImplementation(LogFactory::useLog4J2Logging);
        tryImplementation(LogFactory::useLog4JLogging);
        tryImplementation(LogFactory::useJdkLogging);
        tryImplementation(LogFactory::useNoLogging);
    }

//tryImplementation logConstructor不存在则执行对应的LogFactory::xxxLogging操作
private static void tryImplementation(Runnable runnable) {
        if (logConstructor == null) {
            try {
                runnable.run();
            } catch (Throwable t) {
                // ignore
            }
        }
    }
    
使用Slf4j加载举例
public static synchronized void useSlf4jLogging() {
        setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
}

//根据传入的适配器Class文件调用仅含一个字符串的构造方法,如果实例化成功则加载成功
private static void setImplementation(Class<? extends Log> implClass) {
        try {
            // 获取implClass这个适配器的构造方法
            Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
            // 尝试加载implClass这个适配器,加载失败会抛出异常
            Log log = candidate.newInstance(LogFactory.class.getName());
            if (log.isDebugEnabled()) {
                log.debug("Logging initialized using '" + implClass + "' adapter.");
            }
            // 加载成功,则更新logConstructor字段,记录适配器的构造方法
            logConstructor = candidate;
        } catch (Throwable t) {
            throw new LogException("Error setting Log implementation.  Cause: " + t, t);
        }
    }
    
// Slf4jImpl适配器构造如下
public Slf4jImpl(String clazz) {

    //注意此时的Logger、LoggerFactory包路径为org.slf4j
    Logger logger = LoggerFactory.getLogger(clazz);

    if (logger instanceof LocationAwareLogger) {
      try {
        // check for slf4j >= 1.6 method signature
        logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
        log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
        return;
      } catch (SecurityException | NoSuchMethodException e) {
        // fail-back to Slf4jLoggerImpl
      }
    }

    // Logger is not LocationAwareLogger or slf4j version < 1.6
    log = new Slf4jLoggerImpl(logger);
  }

上面Logger logger = LoggerFactory.getLogger(clazz); 使用的是org.slf4j包中的类,在mybtais源码中通过maven引入slf4j ,通过true控制即不传递依赖,在应用中需要手动依赖org.slf4j包,Slf4jImpl适配器才能加载到Logger logger = LoggerFactory.getLogger(clazz);才能正确实例化,静态方法加载其他适配器也是如此,mybatis源码中的 org.slf4j坐标如下

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.30</version>
  <optional>true</optional>
</dependency>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值