• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Mybatis框架源码笔记(六):Mybatis集成日志框架原理

武飞扬头像
嫣夜来
帮助5

1 Mybatis中集成日志框架示例

1.1 Mybatis使用log4j示例(推荐方式)

第一步:pom.xml引入log4j依赖

<!-- slf4j日志门面 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.36</version>
        </dependency>

        <!-- 引入slf4j对应log4j的桥接器 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.36</version>
        </dependency>

        <!-- log4j日志框架 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

第二步:全局配置文件中配置logImpl的值为"SLF4J"
学新通

第三步:在resource目录下创建一个log4j.properties的配置文件

log4j.rootLogger=debug,console,stdout 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=d:/msb.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n

log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG

OK, 日志框架集成完成, 可以使用了,重新运行Mybatis测试,控制台输出如下:
学新通

1.2 Mybatis使用logback示例

第一步:pom.xml引入logback依赖

 <dependency>
     <groupId>ch.qos.logback</groupId>
     <artifactId>logback-classic</artifactId>
     <version>1.2.11</version>
 </dependency>

第二步:全局配置文件中配置logImpl的值为"SLF4J"
学新通

第三步:在resource目录下创建一个logback.xml的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoder 默认配置为PatternLayoutEncoder -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
            </pattern>
        </encoder>
    </appender>
    <logger name="com.kkarma" level="DEBUG"/>
    <logger name="com.kkarma" level="INFO"/>
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

OK, 日志框架集成完成, 可以使用了,重新运行Mybatis测试,控制台输出如下:
学新通
更多日志框架的集成请参考官方文档:https://mybatis.org/mybatis-3/zh/logging.html

2 Mybatis中集成日志框架原理解析

2.1 关于全局配置文件中日志相关配置的解析过程

学新通
学新通
学新通

学新通
学新通
学新通
学新通

学新通

2.2 Myabtis中关于日志框架的设计

2.2.1 Log接口

/*
 *    Copyright 2009-2022 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.logging;

/**
 * @author Clinton Begin
 * 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);

}

2.2.2 LogFactory

/*
 *    Copyright 2009-2023 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.logging;

import java.lang.reflect.Constructor;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public final class LogFactory {

  /**
   * Marker to be used by logging implementations that support markers.
   * 支持标记的日志记录实现要使用的标记
   */
  public static final String MARKER = "MYBATIS";

  /** 日志构造器 */
  private static Constructor<? extends Log> logConstructor;

  static {
    // 支持Slf4j日志框架
    tryImplementation(LogFactory::useSlf4jLogging);
    // 支持common-logging日志框架
    tryImplementation(LogFactory::useCommonsLogging);
    // 支持log4j2日志框架
    tryImplementation(LogFactory::useLog4J2Logging);
    // 支持log4j日志框架
    tryImplementation(LogFactory::useLog4JLogging);
    // 支持jdk自带日志框架
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

  private LogFactory() {
    // disable construction
  }

  public static Log getLog(Class<?> clazz) {
    return getLog(clazz.getName());
  }

  public static Log getLog(String logger) {
    try {
    	// 通过反射根据传递进来的日志实现类创建Log的动态代理对象
      return logConstructor.newInstance(logger);
    } catch (Throwable t) {
      throw new LogException("Error creating logger for logger "   logger   ".  Cause: "   t, t);
    }
  }

  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 useCommonsLogging() {
    setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
  }

  /**
   * @deprecated Since 3.5.9 - See https://github.com/mybatis/mybatis-3/issues/1223. This method will remove future.
   */
  @Deprecated
  public static synchronized void useLog4JLogging() {
    setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
  }

  public static synchronized void useLog4J2Logging() {
    setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
  }

  public static synchronized void useJdkLogging() {
    setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.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) {
    if (logConstructor == null) {
      try {
        runnable.run();
      } catch (Throwable t) {
        // ignore
      }
    }
  }

  private static void setImplementation(Class<? extends Log> implClass) {
    try {
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      // 通过解析我们在全局配置文件中配置的logImpl的值, 获取到日志框架框架的全限定路径名, 通过动态代理创建出Log接口的代理类对象, 配置的logImpl的实现类是什么类型,最后的log对象就是对应类型的日志框架的实现类对象,这里使用了适配器模式
      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);
    }
  }
}

2.2.3 项目中的应用

2.2.3.1 概述

学新通
看到这张图片,大家在项目中使用Mybatis框架进行数据库操作的时候可能都在控制台看到过类似的日志打印, 大家有没有去深究一下, 这些功能到底是怎么实现的呢, 下面我主要通过源码分析一下在Mybatis框架在执行JDBC操作的时候是如何完成相关的操作信息和SQL语句从控制台输出的?

这里我们聊的是围绕着JDBC相关的操作, 所以我们只分析跟JDBC相关的日志实现。

我们知道JDBC关键的几个实现, 所以要打印日志, 最好的方式就是通过动态代理创建这些关键接口的代理类, 在我们的代理类中完成我们需要的日志打印输出相关的增强功能,Mybatis框架也的确是这样进行设计的。

  • Connection
  • Statement
  • ResultSet

BaseJdbcLogger是一个抽象类,它是jdbc包下其他Logger的父类,因为需要通过动态代理创建实现类对象, 这里的实现类都实现了Invocationhandler接口。继承关系如下:
学新通

2.2.3.2 ConnectionLogger的创建时机和创建过程
  • Connection的代理对象ConnectionLogger的创建时机和创建过程, 如下图
    学新通
    学新通
2.2.3.3 PreparedStatementLogger的创建时机和创建过程

-PreparedStatement的代理对象PreparedStatementLogger的创建时机和创建过程, 如下图
学新通
这里就是日志增强的体现, 我们可以在控制台看到预处理的SQL语句输出。
学新通

学新通
学新通
学新通
学新通

2.2.3.4 ResultSetLogger的创建时机和创建过程
  • ResultSet的代理对象ResultSetLogger对象创建的实际和创建过程如下
    学新通
    从ResultSetWrapper中获取到ResultSetLogger对象
    学新通
    对ResultSet进行循环处理,解析ResultSet中的每一个行数据对象
    学新通真正处理数据库列字段映射到java实体类的属性值得处理方法
    学新通
    当最后一次执行ResultSet.next()方法时, 游标移动到最后一行数据行的下一行, 没有数据返回, foundValues的结果就为false, 说明数据解析映射已经完成了, 控制台输出查询结果数据的总数。
    学新通
2.2.3.5 绘图总结

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfgkgcj
系列文章
更多 icon
同类精品
更多 icon
继续加载