简述

在Java开发中常用的日志框架有Log4j、Log4j2、Apache Commons Log、java.util.logging、slf4j等,这些工具对外的接口并不相同。为了统一这些工具的接口,MyBatis定义了一套统一的日志接口供上层使用,并为上述常用的日志框架提供了相应的适配器。

适配器模式

首先,我们简单介绍设计模式中有六大原则。

单一职责原则: 不要存在多于一个导致类变更的原因,简单来说,一个类只负责唯一项职责。

里氏替换原则: 如果对每一个类型为T1的对象t1,都有类型为T2的对象t2,使得以T1定义的所有程序P在所有的对象t1都代换成t2时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。遵守里氏替换原则,可以帮助我们设计出更为合理的继承体系。

依赖倒置原则: 系统的高层模块不应该依赖低层模块的具体实现,二者都应该依赖其抽象类或接口,抽象接口不应该依赖具体实现类,而具体实现类应该于依赖抽象。简单来说,我们要面向接口编程。当需求发生变化时对外接口不变,只要提供新的实现类即可。

接口隔离原则: 一个类对另一个类的依赖应该建立在最小的接口上。简单来说,我们在设计接口时,不要设计出庞大臃肿的接口,因为实现这种接口时需要实现很多不必要的方法。我们要尽量设计出功能单一的接口,这样也能保证实现类的职责单一。

迪米特法则: 一个对象应该对其他对象保持最少的了解。简单来说,就是要求我们减低类间耦合。

开放-封闭原则: 程序要对扩展开放,对修改关闭。简单来说,当需求发生变化时,我们可以通过添加新的模块满足新需求,而不是通过修改原来的实现代码来满足新需求。

在这六条原则中,开放-封闭原则是最基础的原则,也是其他原则以及后文介绍的所有设计模式的最终目标。

适配器模式的主要目的是解决由于接口不能兼容而导致类无法使用的问题,适配器模式会将需要适配的类转换成调用者能够使用的目标接口。这里先介绍适配器模式中涉及的几个角色:

  • 目标接口(Target):调用者能够直接使用的接口。
  • 源(Adaptee)角色:需要适配的接口
  • 适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

使用适配器模式的好处就是复用现有组件。应用程序需要复用现有的类,但接口不能被该应用程序兼容,则无法直接使用。这种场景下就适合使用适配器模式实现接口的适配,从而完成组件的复用。很明显,适配器模式通过提供Adapter的方式完成接口适配,实现了程序复用Adaptee的需求,避免了修改Adaptee实现接口,这符合“开放-封闭”原则。当有新的Adaptee需要被复用时,只要添加新的Adapter即可,这也是符合“开放-封闭”原则的。

在MyBatis的日志模块中,就使用了适配器模式。Log4j、Log4j2等第三方日志组件对外提供的接口各不相同,MyBatis为了集成和复用这些第三方日志组件,在其日志模块中提供了多种Adapter,将这些第三方日志组件对外的接口适配成了org.apache.ibatis.logging.Log接口,这样MyBatis内部就可以统一通过org.apache.ibatis.logging.Log接口调用第三方日志组件的功能了。

日志适配器

前面提到的多种第三方日志组件都有各自的Log级别,且都有所不同,例如java.util.logging提供了All、FINEST、FINER、FINE、CONFIG、INFO、WARNING等9种级别,而Log4j2则只有trace、debug、info、warn、error、fatal这6种日志级别。MyBatis统一提供了trace、debug、warn、error四个级别,这基本与主流日志框架的日志级别类似,可以满足绝大多数场景的日志需求。 MyBatis的日志模块位于org.apache.ibatis.logging包中,该模块中通过Log接口定义了日志模块的功能,当然日志适配器也会实现此接口。LogFactory工厂类负责创建对应的日志组件适配器

在LogFactory类加载时会执行其静态代码块,其逻辑是按序加载并实例化对应日志组件的适配器,然后使用LogFactory.logConstructor这个静态字段,记录当前使用的第三方日志组件的适配器,

  1. /**
  2. * 记录当前使用的第三方日志组件所对应的适配器的构造方法
  3. */
  4. private static Constructor<? extends Log> logConstructor;
  5. /**
  6. * 调用方法tryImplementation顺序加载每种组件
  7. */
  8. static {
  9. tryImplementation(new Runnable() {
  10. @Override
  11. public void run() {
  12. useSlf4jLogging();
  13. }
  14. });
  15. tryImplementation(new Runnable() {
  16. @Override
  17. public void run() {
  18. useCommonsLogging();
  19. }
  20. });
  21. tryImplementation(new Runnable() {
  22. @Override
  23. public void run() {
  24. useLog4J2Logging();
  25. }
  26. });
  27. tryImplementation(new Runnable() {
  28. @Override
  29. public void run() {
  30. useLog4JLogging();
  31. }
  32. });
  33. tryImplementation(new Runnable() {
  34. @Override
  35. public void run() {
  36. useJdkLogging();
  37. }
  38. });
  39. tryImplementation(new Runnable() {
  40. @Override
  41. public void run() {
  42. useNoLogging();
  43. }
  44. });
  45. }

LogFactory.tryImplementation()方法首先会检测logConstructor字段,若为空则调用Runnable.run()方法

  1. private static void tryImplementation(Runnable runnable) {
  2. if (logConstructor == null) {
  3. try {
  4. runnable.run();
  5. } catch (Throwable t) {
  6. // ignore
  7. }
  8. }
  9. }

每种日志组件的加载都是调用setImplementation方法,这里以Slf4j为例,如下:

  1. public static synchronized void useSlf4jLogging() {
  2. setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  3. }
  4. /**
  5. * 根据指定适配器实现类加载相应的日志组件
  6. */
  7. private static void setImplementation(Class<? extends Log> implClass) {
  8. try {
  9. //获取指定适配器的构造方法
  10. Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
  11. //实例化适配器
  12. Log log = candidate.newInstance(LogFactory.class.getName());
  13. if (log.isDebugEnabled()) {
  14. log.debug("Logging initialized using '" + implClass + "' adapter.");
  15. }
  16. logConstructor = candidate;
  17. } catch (Throwable t) {
  18. throw new LogException("Error setting Log implementation. Cause: " + t, t);
  19. }
  20. }

Mybatis源码学习之日志(五)的更多相关文章

  1. mybatis源码学习:一级缓存和二级缓存分析

    目录 零.一级缓存和二级缓存的流程 一级缓存总结 二级缓存总结 一.缓存接口Cache及其实现类 二.cache标签解析源码 三.CacheKey缓存项的key 四.二级缓存TransactionCa ...

  2. mybatis源码学习:基于动态代理实现查询全过程

    前文传送门: mybatis源码学习:从SqlSessionFactory到代理对象的生成 mybatis源码学习:一级缓存和二级缓存分析 下面这条语句,将会调用代理对象的方法,并执行查询过程,我们一 ...

  3. mybatis源码学习:插件定义+执行流程责任链

    目录 一.自定义插件流程 二.测试插件 三.源码分析 1.inteceptor在Configuration中的注册 2.基于责任链的设计模式 3.基于动态代理的plugin 4.拦截方法的interc ...

  4. Mybatis源码学习第六天(核心流程分析)之Executor分析

    今Executor这个类,Mybatis虽然表面是SqlSession做的增删改查,其实底层统一调用的是Executor这个接口 在这里贴一下Mybatis查询体系结构图 Executor组件分析 E ...

  5. mybatis源码学习(一) 原生mybatis源码学习

    最近这一周,主要在学习mybatis相关的源码,所以记录一下吧,算是一点学习心得 个人觉得,mybatis的源码,大致可以分为两部分,一是原生的mybatis,二是和spring整合之后的mybati ...

  6. Mybatis源码学习第八天(总结)

    源码学习到这里就要结束了; 来总结一下吧 Mybatis的总体架构 这次源码学习我们,学习了重点的模块,在这里我想说一句,源码的学习不是要所有的都学,一行一行的去学,这是错误的,我们只需要学习核心,专 ...

  7. Mybatis源码学习之整体架构(一)

    简述 关于ORM的定义,我们引用了一下百度百科给出的定义,总体来说ORM就是提供给开发人员API,方便操作关系型数据库的,封装了对数据库操作的过程,同时提供对象与数据之间的映射功能,解放了开发人员对访 ...

  8. MyBatis源码解析之日志记录

    一 .概述 MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,但MyBatis统一提供了trace.debug.warn.error四个级 ...

  9. mybatis源码学习(三)-一级缓存二级缓存

    本文主要是个人学习mybatis缓存的学习笔记,主要有以下几个知识点 1.一级缓存配置信息 2.一级缓存源码学习笔记 3.二级缓存配置信息 4.二级缓存源码 5.一级缓存.二级缓存总结 1.一级缓存配 ...

随机推荐

  1. Django中ORM常用字段及字段参数

    Object Relational Mapping(ORM) ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据 ...

  2. 开始学Python 啦 ,持续不断总结中。。(转)快捷键的使用

    最重要的快捷键1. ctrl+shift+A:万能命令行2. shift两次:查看资源文件新建工程第一步操作1. module设置把空包分层去掉,compact empty middle packag ...

  3. C#picturebox控件图片以json格式上传java后台保存

    关于winform上传图片到Java后端,保存到数据库,有多种方法,本文主要介绍利用picturebox控件,点击按钮上传图片,将图片转化为base64格式,以json格式上传到Java后台,再从ja ...

  4. c#OpenCVSharp+Zxing识别条形码

    参考博客:https://www.cnblogs.com/dengxiaojun/p/5278679.html,但是他的demo下载太贵了 可以下载这个https://download.csdn.ne ...

  5. SSM框架警告/错误集合

    警告: 1.使用Eclipse的Spring Elements组件的时候发现会提示有警告:Expect at least one bean match() 解决办法:项目可以正常运行,未有报错,在其他 ...

  6. K2 BPM_曾经我也是996的一员_全球领先的工作流引擎

    最近关于996的工作模式掀起了新一波讨论热潮.事情源于有人在知名代码托管平台GitHub上,发起了一个名为“996.ICU”的项目,意为“工作996,生病ICU”,以抵制互联网公司的996工作制,项目 ...

  7. Xcode8警告⚠️ Empty paragraph passed to '@xxx' command

    问题 Xcode8升级后,之前添加的注释会有很多警告 解决方法 基础知识,就是在编译选项中,添加警告屏蔽 解决步骤 显示警告信息 显示警告信息.png 查看警告类型 查看警告类型.png 屏蔽警告 W ...

  8. 使用postman创建Marketing Cloud的Contact

    首先在Marketing Cloud的UI上创建一个contact: 观察Chrome开发者工具network标签页里的HTTP请求: https://jerry.gcdemo.hybris.com/ ...

  9. PHP中pdo的使用

    <?php /** *下面代码中information为表名 * */ //1.先要连数据库 $pdo=new PDO('mysql:host=localhost;dbname=数据库名','用 ...

  10. Python学习记录6-list、tuple、dict、set复习

    数据类型在一门语言中是非常重要的,所以选择再次学习一下加深记忆.本次主要参考了大神廖雪峰的官方网站,非常感谢大神,讲的很清晰,收获很大. 标准数据类型 Number(数字) String(字符串) L ...