Mybatis源码学习之日志(五)
简述
在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这个静态字段,记录当前使用的第三方日志组件的适配器,
/**
* 记录当前使用的第三方日志组件所对应的适配器的构造方法
*/
private static Constructor<? extends Log> logConstructor;
/**
* 调用方法tryImplementation顺序加载每种组件
*/
static {
tryImplementation(new Runnable() {
@Override
public void run() {
useSlf4jLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useCommonsLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useLog4J2Logging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useLog4JLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useJdkLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useNoLogging();
}
});
}
LogFactory.tryImplementation()方法首先会检测logConstructor字段,若为空则调用Runnable.run()方法
private static void tryImplementation(Runnable runnable) {
if (logConstructor == null) {
try {
runnable.run();
} catch (Throwable t) {
// ignore
}
}
}
每种日志组件的加载都是调用setImplementation方法,这里以Slf4j为例,如下:
public static synchronized void useSlf4jLogging() {
setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
}
/**
* 根据指定适配器实现类加载相应的日志组件
*/
private static void setImplementation(Class<? extends Log> implClass) {
try {
//获取指定适配器的构造方法
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
//实例化适配器
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);
}
}
Mybatis源码学习之日志(五)的更多相关文章
- mybatis源码学习:一级缓存和二级缓存分析
目录 零.一级缓存和二级缓存的流程 一级缓存总结 二级缓存总结 一.缓存接口Cache及其实现类 二.cache标签解析源码 三.CacheKey缓存项的key 四.二级缓存TransactionCa ...
- mybatis源码学习:基于动态代理实现查询全过程
前文传送门: mybatis源码学习:从SqlSessionFactory到代理对象的生成 mybatis源码学习:一级缓存和二级缓存分析 下面这条语句,将会调用代理对象的方法,并执行查询过程,我们一 ...
- mybatis源码学习:插件定义+执行流程责任链
目录 一.自定义插件流程 二.测试插件 三.源码分析 1.inteceptor在Configuration中的注册 2.基于责任链的设计模式 3.基于动态代理的plugin 4.拦截方法的interc ...
- Mybatis源码学习第六天(核心流程分析)之Executor分析
今Executor这个类,Mybatis虽然表面是SqlSession做的增删改查,其实底层统一调用的是Executor这个接口 在这里贴一下Mybatis查询体系结构图 Executor组件分析 E ...
- mybatis源码学习(一) 原生mybatis源码学习
最近这一周,主要在学习mybatis相关的源码,所以记录一下吧,算是一点学习心得 个人觉得,mybatis的源码,大致可以分为两部分,一是原生的mybatis,二是和spring整合之后的mybati ...
- Mybatis源码学习第八天(总结)
源码学习到这里就要结束了; 来总结一下吧 Mybatis的总体架构 这次源码学习我们,学习了重点的模块,在这里我想说一句,源码的学习不是要所有的都学,一行一行的去学,这是错误的,我们只需要学习核心,专 ...
- Mybatis源码学习之整体架构(一)
简述 关于ORM的定义,我们引用了一下百度百科给出的定义,总体来说ORM就是提供给开发人员API,方便操作关系型数据库的,封装了对数据库操作的过程,同时提供对象与数据之间的映射功能,解放了开发人员对访 ...
- MyBatis源码解析之日志记录
一 .概述 MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,但MyBatis统一提供了trace.debug.warn.error四个级 ...
- mybatis源码学习(三)-一级缓存二级缓存
本文主要是个人学习mybatis缓存的学习笔记,主要有以下几个知识点 1.一级缓存配置信息 2.一级缓存源码学习笔记 3.二级缓存配置信息 4.二级缓存源码 5.一级缓存.二级缓存总结 1.一级缓存配 ...
随机推荐
- C++单链表类(带头结点)
Link.h #ifndef _LINK_0411 #define _LINK_0411 #include <string> #include <iostream> //定义数 ...
- CSS设置浮动导致背景颜色设置无效的解决方法
float浮动会使父元素高度塌陷,父级元素不能被撑开,所以导致背景颜色不能被撑开 解决方法: 对父元素设置高度 对父元素设置 overflow:hidden清除浮动 把父元素也设置为float浮动 结 ...
- Java虚拟机内存基础、垃圾收集算法及JVM优化
1 JVM 简单结构图 1.1 类加载子系统与方法区 类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称 为方法区的内存空间.除了类的信息外,方法区中可能还会存放 ...
- 多进程编程——理论讲解与 multiprocessing 模块
多进程编程 import os pid = os .fork() 功能 :创建新的进程 参数: 无 返回值 :失败返回一个负数 成功:在原有进程中返回新的进程的PID号 在新进程中返回为0* 子进程会 ...
- idea代码爆红,clean,或者maven reimport都不起作用
1 突然自己的idea的Maven项目代码都是爆红,但是可以运行,添加新的代码确无法运行 尝试了clean,或者reimport,甚至是大家推荐的,刷新缓存重启也没有作用 2 检查项目的jdk配置,也 ...
- redis——redis的一些核心把握
redis单线程,为什么比较快 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程.redis能够快速执行的原因有三点: (1) 绝大 ...
- Java工程师面试题集锦
即将踏上找工作的征途,参考网上面试题库准备一波面试题,希望能找到理想中的工作,愿一切顺利. 一.Java基础 1.String类为什么是final的. 2.HashMap的源码,实现原理,底层结构. ...
- PHP 按照指定数量分割数组
<?php /** * 系统辅助类 * @date 2019年7月2日 * @comment * */ class SystemUtils { private static $_instance ...
- 关于swap交换操作的新方法
swap: 在oi中,swap用于交换两个变量的数值. 初学oi时,我们这样操作: 也就是说,需要一个temp变量来寄存x或y的值,因为当一个变量被赋值成为另一个变量时,没有temp它的值会丢失. 貌 ...
- python内置函数(二)之filter,map,sorted
filter filter()函数接收一个函数 f 和一个iterable的对象,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条 ...