热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

MyBatis源码分析之日志logging详解

这篇文章主要给大家介绍了关于MyBatis源码分析之日志logging的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

本文介绍个人对 logging 包下源码的理解。分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧

logging 配置加载

我们先从日志的配置加载开始阅读, MyBatis 的各项配置的加载过程都可以从 XMLConfigBuilder 类中找到,我们定位到该类下的日志加载方法 loadCustomLogImpl:

private void loadCustomLogImpl(Properties props) {
 // 从 MyBatis 的 TypeAliasRegistry 中查找 logImpl 键所对应值的类对象
 // 这里 logImpl 对应的 value 值可以从 org.apache.ibatis.session.Configuration 的构造方法中找到
 // 注意 Log 类,这是 MyBatis 内部对日志对象的抽象
 Class<&#63; extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
 // 将查找到的 Class 对象设置到 Configuration 对象中
 configuration.setLogImpl(logImpl);
}

很简单的一个方法,每行都有注释,其中 configuration.setLogImpl() 里面调用了 LogFactory.useCustomLogging() ,这出现了新类 LogFactory 类,接下来我们就来聊聊这个类。

LogFactory

useCustomLogging()方法

LogFactory 是框架内部获取 Log 对象的手段,通过它的名字也能看出来。它有如下几类方法:

// 注意这个类型的方法都是同步方法
public synchronized static useXxxLogging(...);

public static Log getLog(...);

private static tryImplementation(Runnable);

private static setImplementation(Class);

刚刚我们看到被调用的方法 useCustomLogging() 方法,是用来设置内部使用的日志框架, MyBatis 自身已经适配了一些常见的日志框架,如 Slf4j 、 Commons Log 、 Log4j 等等。

useCustomLogging() 方法内部调用 setImplementation(Class) 方法,此方法代码如下,功能简单:

private static void setImplementation(Class<&#63; extends Log> implClass) {
 try {
  // 获取 Log实现类的构造方法,它只有一个字符串作为参数
  Constructor<&#63; extends Log> candidate = implClass.getConstructor(String.class);

  // 创建一个 Log 对象,打印 debug 日志
  Log log = candidate.newInstance(LogFactory.class.getName());
  if (log.isDebugEnabled()) {
   log.debug("Logging initialized using '" + implClass + "' adapter.");
  }

  // ...
  // 把 candidate 对象设置到 LogFactory 的静态变量 logConstructor,这个静态变量在 getLog() 方法
  // 中被用到
  logCOnstructor= candidate;
 } catch (Throwable t) {
  throw new LogException("Error setting Log implementation. Cause: " + t, t);
 }
}

Log 接口

刚刚我们接触到了 Log 这个类,它是一个接口,是 MyBatis 内部使用的日志对象的抽象,它是为了兼容市面上各种各样的日志框架,使用了适配器模式,通过 Log 接口来连接 MyBatis 和其他日志框架,通过实现 Log 接口连着 MyBatis 和需要适配的日志框架。

Log 接口代码如下,先试着发现该接口与其他常见的日志接口的区别:

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

}

可有发现?Log 接口缺少了 info 级别的日志输出方法,个人猜测应该是 MyBatis 内部不需要 info 级别的日志输出,毕竟 Log 接口设计之初就是为了内部使用,而框架使用者是不会采用 MyBatis 的日志作为系统的日志。注意一点: 实现了 Log 接口的类必须拥有一个参数只有一个字符串的构造方法 ,MyBatis 就是通过这个构造方法创建日志对象的。

MyBatis 适配了许多常见的日志框架,这里就单单介绍 Log4jImpl 类,它代码非常简单:

public class Log4jImpl implements Log {

 private static final String FQCN = Log4jImpl.class.getName();

 // 这里包装了 Log4j 框架的日志对象,从而实现适配
 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);
 }

}

tryImplementation() 方法

LogFactory 类再介绍一下被静态代码块使用的方法 tryImplementation(Runnable) 。静态代码块代码如下:

static {
 // 依次执行如下代码,当没有该类会抛 ClassNotFoundException ,然后继续执行
 tryImplementation(LogFactory::useSlf4jLogging);
 tryImplementation(LogFactory::useCommonsLogging);
 tryImplementation(LogFactory::useLog4J2Logging);
 tryImplementation(LogFactory::useLog4JLogging);
 tryImplementation(LogFactory::useJdkLogging);
 tryImplementation(LogFactory::useNoLogging);
}

这个方法有点迷惑性,因为它使用 Runnable 接口作为参数,而 useXxxLOgging() 方法又是同步方法,很容易联想到多线程,实际上这里并没有, Runnable 接口不结合 Thread 类使用它就是一个普通的函数接口。除去这些就没什么了,不过是调用了 Runnable 的 run() 方法而已。

getLog() 方法

getLog() 没什么多说的,就是通过反射创建 Log 接口实现类,这里没有使用到缓存,每次调用都是创建一个新的对象。

jdbc 包

这个包与其他包有些不同,它的职能是为各个阶段的流程提供日志打印,该包一共就五个类,它们的关系如下:

除了 BaseJdbcLogger 类其他类都实现了 InvocationHandler 接口,这个接口是 JDK 提供的动态代理接口,所以显而易见可以知道它们就是通过代理在各个阶段打印相应的日志。

以下为 BaseJdbcLogger 类的部分说明:

  • SET_METHODS:静态字段,记录 PreparedStatement 中 set 开头的的方法名
  • EXECUTE_METHODS:静态字段,记录 SQL 执行的方法名
  • columnXxx:实例字段,记录 SQL 参数信息
  • statementLog:日志对象
  • queryStack:查询栈数
  • getParameterValueString():将 SQL 参数转为一个字符串
  • removeBreakingWhitespace():移除 SQL 中多余的空白字符
  • prefix():获取前缀 ==>/<==

而其余四个类都是简单的逻辑:判断执行的方法是否为指定方法,然后打印相应的日志。

总结

以上便是个人研究 logging 包的内容,本包使用了以下设计模式(包含不限于):

  • 适配器模式
  • 代理模式

其中适配器模式应该是一个比较不错的示例,可做参考。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。


推荐阅读
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了通过mysql命令查看mysql的安装路径的方法,提供了相应的sql语句,并希望对读者有参考价值。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Android日历提醒软件开源项目分享及使用教程
    本文介绍了一款名为Android日历提醒软件的开源项目,作者分享了该项目的代码和使用教程,并提供了GitHub项目地址。文章详细介绍了该软件的主界面风格、日程信息的分类查看功能,以及添加日程提醒和查看详情的界面。同时,作者还提醒了读者在使用过程中可能遇到的Android6.0权限问题,并提供了解决方法。 ... [详细]
author-avatar
筱杰丶Jevon_879
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有