1.为什么需要使用适配器?
      集成第三方日志组件,屏蔽日志组件底层实现,统一提供写日志的接口。

  2.什么是适配器模式
   定义:将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作。

  

  client-->Target(统一接口) Adapter继承Target,并封装Adaptee对象 Adaptee类做具体的工作
  Target目标角色:
  该角色定义把其他类转换为何种接口
  Adaptee源角色:
  你想把谁转换成目标角色,这个“谁”就是源角色,他是已经存在的角色。
  Adapter 适配器角色:
  适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责是把源角色转换为目标角色,实现方式是通过继承或者组合的方式.

  3.mybatis中日志适配

   从commons包到stdout都可视为适配器角色,Log接口为目标角色,LogFactory为创建具体日志组件适配器的工厂。

  适配关系如下:

  

  从源码角度看:
  目标角色: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);
}

  

  统一定义了日志级别。
  适配器角色,以Jdk14LoggingImpl 为例,

  

package org.apache.ibatis.logging.jdk14;

import java.util.logging.Level;
import java.util.logging.Logger; import org.apache.ibatis.logging.Log; /**
* @author Clinton Begin
*/
public class Jdk14LoggingImpl implements Log { private final Logger log; public Jdk14LoggingImpl(String clazz) {
log = Logger.getLogger(clazz);
} @Override
public boolean isDebugEnabled() {
return log.isLoggable(Level.FINE);
} @Override
public boolean isTraceEnabled() {
return log.isLoggable(Level.FINER);
} @Override
public void error(String s, Throwable e) {
log.log(Level.SEVERE, s, e);
} @Override
public void error(String s) {
log.log(Level.SEVERE, s);
} @Override
public void debug(String s) {
log.log(Level.FINE, s);
} @Override
public void trace(String s) {
log.log(Level.FINER, s);
} @Override
public void warn(String s) {
log.log(Level.WARNING, s);
} }

  

  Jdk14LoggingImpl 通过继承关系实现Log,组合java.util.logging.Logger对象log; ,适配器实现继承的方法的时候通过关联对象log实现。
  4.mybatis怎么初始化日志适配器

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 {
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();
}
});
} private LogFactory() {
// disable construction
} public static Log getLog(Class<?> aClass) {
return getLog(aClass.getName());
} public static Log getLog(String logger) {
try {
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);
} 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);
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 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
  SLF4J
  Apache Commons Logging
  Log4j 2
  Log4j
  JDK logging
  MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。

  这段话和源码中的static静态代码块相对应。

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

  

  官网中的说明顺序一样,
  以其中JdkLogging为例说明初始化日志适配过程:

private static void tryImplementation(Runnable runnable) {
if (logConstructor == null) {
try {
runnable.run();
} catch (Throwable t) {
// ignore
}
}
}

  首先判断当前logConstructor日志适配器构造方法是否已经初始化,如果初始化则返回,如果没有初始化则执行run()方法,对应当前示例就是useJdkLogging()方法,

  

public static synchronized void useJdkLogging() {
交给了setImplementation
setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
}
private static void setImplementation(Class<? extends Log> implClass) {
try {
//通过传入类型获取适配器构造方法 如果没有找到对应类型则返回null
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);
}
}

  

  按上面次序进行日志适配器的初始化。

  最后一个useNoLogging代表禁用日志功能,因为适配器NoLoggingImpl中什么都没有做。

package org.apache.ibatis.logging.nologging;

import org.apache.ibatis.logging.Log;

/**
* @author Clinton Begin
*/
public class NoLoggingImpl implements Log { public NoLoggingImpl(String clazz) {
// Do Nothing
} @Override
public boolean isDebugEnabled() {
return false;
} @Override
public boolean isTraceEnabled() {
return false;
} @Override
public void error(String s, Throwable e) {
// Do Nothing
} @Override
public void error(String s) {
// Do Nothing
} @Override
public void debug(String s) {
// Do Nothing
} @Override
public void trace(String s) {
// Do Nothing
} @Override
public void warn(String s) {
// Do Nothing
}
}

官网说明第二段
   你也可以调用以下任一方法来选择日志实现:

org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
org.apache.ibatis.logging.LogFactory.useLog4JLogging();
org.apache.ibatis.logging.LogFactory.useJdkLogging();
org.apache.ibatis.logging.LogFactory.useCommonsLogging();
org.apache.ibatis.logging.LogFactory.useStdOutLogging()

你应该在调用其它 MyBatis 方法之前调用以上的某个方法。另外,仅当运行时类路径中存在该日志实现时,日志实现的切换才会生效。
如果你的环境中并不存在 Log4J,你却试图调用了相应的方法,MyBatis 就会忽略这一切换请求,并将以默认的查找顺序决定使用的日志实现。

因为上面的方法为LogFactory中的静态方法,所以可以直接调用来初始化适配器工厂中的适配器构造方法(logConstructor),从而实现第三方日志组件的切换,并且该方法执行于static静态代码块之后。

本文参考:mybatis技术内幕,Mybatis官网

mybatis源码解析-日志适配器的更多相关文章

  1. myBatis源码解析-日志篇(1)

    上半年在进行知识储备,下半年争取写一点好的博客来记录自己源码之路.在学习源码的路上也掌握了一些设计模式,可所谓一举两得.本次打算写Mybatis的源码解读. 准备工作 1. 下载mybatis源码 下 ...

  2. 【学习转载】MyBatis源码解析——日志记录

    声明:转载自前辈:开心的鱼a1 一 .概述 MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,但MyBatis统一提供了trace.deb ...

  3. mybatis源码-解析配置文件(三)之配置文件Configuration解析

    目录 1. 简介 1.1 系列内容 1.2 适合对象 1.3 本文内容 2. 配置文件 2.1 mysql.properties 2.2 mybatis-config.xml 3. Configura ...

  4. Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例

    在Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们看到了XMLConfigBuilder(xml配置解析器)的实例化.而且这个实例化过程在文章:Mybatis源码解析,一步一步从浅 ...

  5. Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析

    在上一篇文章Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例中我们谈到了properties,settings,envir ...

  6. Mybatis源码解析(二) —— 加载 Configuration

    Mybatis源码解析(二) -- 加载 Configuration    正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...

  7. MyBatis 源码篇-日志模块1

    在 Java 开发中常用的日志框架有 Log4j.Log4j2.Apache Common Log.java.util.logging.slf4j 等,这些日志框架对外提供的接口各不相同.本章详细描述 ...

  8. 【MyBatis源码解析】MyBatis一二级缓存

    MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...

  9. Mybatis源码解析-DynamicSqlSource和RawSqlSource的区别

    XMLLanguageDriver是ibatis的默认解析sql节点帮助类,其中的方法其会调用生成DynamicSqlSource和RawSqlSource这两个帮助类,本文将对此作下简单的简析 应用 ...

随机推荐

  1. Vue+Vuex实现自动登录 升级版

    Vue+Vuex实现自动登录 升级版 之前实现的版本在这里:Vue+Vuex实现自动登录       在之前实现的版本中,如果你进行测试,可以看到在浏览器的local Storage中,确实里面有了我 ...

  2. Reflux之Store

    Reflux中的Store既是一个listener(既有对action的监听,又有对store的监听)同时又是一个publisher. 一.监听单个action const Reflux = requ ...

  3. scrapy中间件之下载中间件使用(网易新闻爬取)

    scrapy项目中的middlewarse.py中间件 爬虫中间件:目前先不介绍 下载中间件(需要在settings.py中开启) (1)请求处理函数:process_request(self, re ...

  4. scrapy框架简介与安装启动

    Scrapy 是一个专业的.高效的爬虫框架,它使用专业的 Twisted 包(基于事件驱动的网络引擎包)高效地处理网络通信,使用 lxml(专业的 XML 处理包).cssselect 高效地提取 H ...

  5. 异步http接口调用库:httpx

    谈到http接口调用,Requests大家并不陌生,例如,robotframework-requests.HttpRunner等HTTP接口测试库/框架都是基于它开发.这里将介绍另一款http接口测试 ...

  6. mysql 一台服务器中装两个mysql

    个人经验: 服务器中已有mysql5.0 现要安装mysql5.5 下载安装包,安装后,mysql5.5中没有my.ini文件,就在我自己的电脑上复制了mysql5.5的my.ini文件进去. 1.在 ...

  7. iozone测试报错:Error writing block 12634, fd= 3 write: No space left on device

    问题:使用iozone测试GFS的读写性能的时候,一直报错Error writing block 12634, fd= 3 write: No space left on device,百思不得其解: ...

  8. Fabric进阶(二)—— 在已有组织中增加节点

    fabric网络在创建时就已经确定了初始的节点数量,而在实际应用场景中可能会需要在某个组织中动态增加节点.这里以balance-transfer v1.0为例(2 Org,4 Peer),介绍如何在o ...

  9. Spring Boot 教程 (4) - swagger-ui

    Spring Boot 教程 - swagger-ui 1. 什么是Swagger? Swagger™的目标是为REST APIs 定义一个标准的,与语言无关的接口,使人和计算机在看不到源码或者看不到 ...

  10. Web-Security-Learning

    Web Security sql注入 MySql MySQL False 注入及技巧总结 MySQL 注入攻击与防御 sql注入学习总结 SQL注入防御与绕过的几种姿势 MySQL偏门技巧 mysql ...