MyBatis基础支持层位于 Mybatis 整体架构的最底层,支撑着 Mybatis 的核心处理层,是整个框架的基石。基础支持层中封装了多个较为通用的、独立的模块,不仅仅为 Mybatis 提供基础支撑,也可以在合适的场景中直接复用。
这篇文章介绍MyBatis的日志模块
在 Java 开 发 中常用的日志框 架有 Log4j
、Log4j2
、Apache Commons Log
、java.util.logging
、 slf4j
等,这 些工具对 外的接口不尽 相同。
为 了统 一这 些工具的接口,MyBatis定义 了一套统 一 的日志接口供上层 使用,并 为 上述常用的日志框 架提供了相应 的适配器。
适配器模式
先简单介绍一下设计模式中的六大原则
单一职责原则
不要存在多于一个导致类变更的原因,简单来说,一个类只负责唯一 项职责。
里氏替换原则
如果对 每一个 类 型为 T1的对 象tl,都有类 型为 T2的对 象使得以 T1定义 的所有程序P在所有的对 象tl都代换 成t2时 ,程序P 的行为 没 有发 生变 化, 那么 类 型T2是类 型T1的子类 型。遵守里氏替换 原则 ,可以帮 助我们 设 计 出更为 合 理的继 承体 系。
依赖倒置原则
系统的高层模块不应该依赖低层模块的具体实现,二者都应该依赖其 抽象类 或接口,抽象接口不应 该 依赖 具体 实 现 类 ,而具体 实 现 类 应 该 于依赖 抽象。简 单 来 说 ,我们 要面向接口编 程。当 需求发 生变 化时 对 外接口不变 ,只要提供新的实 现 类 即 可。
接口隔离原则
一个类对另一个类的依赖应该建立在最小的接口上。简单来说,我们 在设 计 接口时 ,不要设 计 出庞 大臃 肿 的接口,因为 实 现 这 种 接口时 需要实 现 很 多不必 要的方法。我们 要尽 量设 计 出功能单 一的接口,这 样 也能保证 实 现 类 的职 责 单 一。
迪米特法则
一个对象应该对其他对象保持最少的了解。简单来说,就是要求我们减 低类 间 耦 合。
开放-封闭原则
程序要对扩展开放,对修改关闭。简单来说,当需求发生变化时,我 们 可以通过 添加新的模块 满 足新需求,而不是通过 修改原来 的实 现 代码 来 满 足新需求。
在这 六条 原则 中,开 放-封闭 原则 是最基础 的原则 ,也是其他原则 以及后文介绍 的所有设 计 模式的最终 目标 。
适配器模式的主要目的是解决 由于接口不能兼容而导 致类 无 法使用的问 题 ,适配器模式会 将 需要适配的类 转 换 成调 用者能够 使用的目标 接口。这 里先介绍 适配器模式中涉及的几 个 角色,如下所述。
目标接口
用者能够 直接使用的接口。
需要适配的类(Adaptee)
—般情况下,Adaptee类中有真正的业务逻辑,但是其接口 不能被调 用者直接使用。
适配器(Adapter)
Adapter 实 现 了 Target 接口,并 包装 了一个 Adaptee 对 象。Adapter 在实 现 Target接口中的方法时 ,会 将 调 用委托给 Adaptee对 象的相关 方法,由Adaptee 完成具体 的业 务 。
适配器模式类图:
使用适配器模式的好处 就是复 用现 有组 件。
应 用程序需要复 用现 有的类 ,但接口不能被该 应 用程序兼容,则 无法直接使用。
这 种 场 景下就适合使用适配器模式实 现 接口的适配,从 而完 成组 件的复 用。
很 明显 ,适配器模式通过 提供Adapter的方式完成接口适配,实 现 了程序复 用 Adaptee的需求,避免了修改Adaptee实 现 接口,这 符合“开 放-封闭 ”原则 。
当 有新的Adaptee 需要被复 用时 ,只要添加新的Adapter即 可,这 也是符合“开 放-封闭 ”原则 的。
在 MyBatis的日志模块 中,就使用了适配器模式。
MyBatis内 部调 用其日志模块 时 ,使用 了其内 部接口(也就是后面要介绍 的org.apache.ibatis.loggingLog
接口)。但是 Log4j
、Log4j2
等第三方日志组 件对 外提供的接口各不相同,MyBatis为 了集成和复 用这 些第三方日志组 件,
在其日志模块 中提供了多种 Adapter
, 将 这 些第三方日志组 件对 外的接口适配成了 org.apache.ibatis.logging.Log
接口,这 样 MyBatis 内 部就可以统 一通过 org.apache.ibatis.logging.Log
接口调 用第三方日志组 件的功能了。
日志适配器
MyBatis 统 一提供了 trace
、debug
、 warn
、error
四个 级 别 用于对应上诉的各种第三方日志组件的级别,这 基本与 主流日志框 架的曰志级 别 类 似,可以满 足绝 大多数 场 景的日志 需求。
MyBatis的日志模块 位于org.apache.ibatis.logging
包中,该 模块 中通过 Log接口定义 了日志 模块 的功能,当 然日志适配器也会 实 现 此接口。LogFactory工厂 类 负 责 创 建对 应 的日志组 件适 配器。
在LogFactory类 加载 时 会 执 行其静 态 代码 块 ,其逻 辑 是按序加载 并 实 例化对 应 日志组 件的 适配器,然后使用LogFactory. logConstructor
这 个 静 态 字段,记 录 当 前使用的第三方日志组 件的 适配器,具体 代码 如下所示。
private static Constructor<? extends Log> logConstructor; |
LogFactory.tryImplementation()
方 法 首 先 会 检 测 logConstructor
字 段 , 若 为 空 则 调 用 Runnable.run()
方法(注意,不是***start()***方法),如上述代码 所示,其中会 调 用use*Logging()
方法。 这 里以useJdkLogging()
为 例进 行介绍 ,具体 代码 如下:
public static synchronized void useJdkLogging() { |
相应的实现类都在org.apache.ibatis.logging
的子包下。且都实 现 了 org.apache.ibatis.logging.Log
接口,并 封装 了 java.util.logging. Logger
对 象 ,org.apache.ibatis.logging.Log
接口 的功能全部通过 调 用 java.util.logging.Logger
对 象 实 现 ,这 与 前面介绍 的适配器模式完全一致。
JDBC调试
在MyBatis的日志模块 中有一个 Jdbc包,它 并 不是将 日志信息通过 JDBC保存到数 据库 中, 而是通过 JDK动 态 代理的方式,将 JDBC操作通过 指定的日志框 架打印出来 。这 个 功能通常在 开 发 阶 段使用,它 可以输 出SQL语 句、用户 传 入的绑 定参 数 、SQL语 句影响 行数 等等信息,对 调 试 程序来 说 是非常重要的。
BaseJdbcLogger是一个 抽象类 ,它 是Jdbc包下其他Logger类 的父类 ,继 承关 系如图所示。
参考
《MyBatis技术内幕》
部分图片来源——《MyBatis技术内幕》