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. IDEA插件记录

    IDEA个性化设置 1. 开发工具 Free MyBatis plugin 作用:可以快速的在mybatis 的mapper 文件和xml文件中快速切换 Lombok 作用:为POJO类添加@Data ...

  2. 用实例理解k8s群集(干货)

    一些概念: 1. pods是一组容器的集合,以pods为单位来管理,共享PID,网络IP和CUTS命名空间: 2. 容器共享存储卷:用yml文件来定义容器,是k8s里的最小单位. 3.本实验要先准备好 ...

  3. mpvue从一无所有开始仿大众点评小程序

    最近尝试了下用mpvue框架开发小程序,它是基于vue开发的. 官方介绍: mpvue 是一个使用 Vue.js 开发小程序的前端框架.框架基于 Vue.js 核心,mpvue 修改了 Vue.js ...

  4. 手写一个简版 asp.net core

    手写一个简版 asp.net core Intro 之前看到过蒋金楠老师的一篇 200 行代码带你了解 asp.net core 框架,最近参考蒋老师和 Edison 的文章和代码,结合自己对 asp ...

  5. js动态添加iframe,自适应页面宽高

    function showIframe(url,w,h){ //添加iframe var if_w = w; var if_h = h; $("<iframe width='" ...

  6. C盘清理.bat

    将以下代码复制到.txt文件中并改为.bat文件运行即可. @echo offecho 正在清除系统垃圾文件,请稍等......del /f /s /q %systemdrive%\*.tmpdel ...

  7. SpringMVC笔记总结

    文章所有代码见:gitee 1.回顾MVC 1.1.什么是MVC MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范. 是将业务逻辑.数据.显示分离 ...

  8. 快服务常见TOP3审核雷区,再不过审就要崩溃啦!

    仰天大笑出门去,审核提交很神气. 垂死病中惊坐起,突然驳回伤不起. 江州司马青衫湿,重新修改苦自知. 梦里花落知多少,回复马上就改好. 审批被驳回可能是最常见的令开发者头大的问题了,明明看起来&quo ...

  9. oracle 11g 用户名和密码默认区分大小写

    oracle 11g 用户名和密码默认区分大小写,可更改alter system set sec_case_sensitive_logon=false 设置改为不区分大小写.

  10. C# 数据操作系列 - 18 让Dapper更强的插件

    0. 前言 在前一篇中我们讲到了Dapper的应用,但是给我们的感觉Dapper不像个ORM更像一个IDbConnection的扩展.是的,没错.在实际开发中我们经常用Dapper作为对EF Core ...