上一章的案例,配置日志级别为 debug,执行一个简单的查询操作,会将 JDBC 操作打印出来。本章通过 MyBatis 日志部分源码分析它是如何实现日志打印的。

在 MyBatis 的日志模块中有一个 jdbc package,package 中的内容如下图所示:

BaseJdbcLogger 是一个抽象类,它是 jdbc package 下其他类的父类,类继承关系如下图所示:

BaseJdbcLogger 类中定义了一些公共集合和简单的工具方法,提供给子类使用。

BaseJdbcLogger 的子类有如下特性:

  • ConnectionLogger:Connection 的代理类,封装了 Connection 对象,继承了 BaseJdbcLogger 抽象类并实现了 InvocationHandler 接口,newInstance() 方法会为其封装的 Connection 对象创建相应的代理对象;
  • PreparedStatementLogger:PreparedStatement 的代理类,封装了 PreparedStatement 对象,继承了 BaseJdbcLogger 抽象类并实现了 InvocationHandler 接口,newInstance() 方法的实现与 Connection 的类似;
  • StatementLogger:与 PreparedStatementLogger 类似;
  • ResultSetLogger:ResultSet 的代理类,封装了 ResultSet 对象,继承了 BaseJdbcLogger 抽象类并实现了 InvocationHandler 接口,newInstance() 方法的实现与 Connection 的类似;

MyBatis 就是通过动态代理的方式,对 JDBC 原生类进行了一层封装,在代理类的 invoke 方法中添加对应 JDBC 操作的日志打印功能。

ConnectionLogger 的实现如下:

public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {

  private final Connection connection;

  private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.connection = conn;
} @Override
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
// 如果调用的是从Object继承的方法,则直接调用,不做任何其他处理
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// 如果调用的是prepareStatement()方法、prepareCall()方法或createStatement()方法
// 则在创建相应的statement对象后,为其创建代理对象并返回该代理对象
if ("prepareStatement".equals(method.getName())) {
// 输出日志
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
// 调用Connection的prepareStatement()方法,得到PreparedStatement对象
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
// 为PreparedStatement对象创建代理对象
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("prepareCall".equals(method.getName())) {
// 输出日志
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("createStatement".equals(method.getName())) {
Statement stmt = (Statement) method.invoke(connection, params);
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else {
return method.invoke(connection, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} /*
* Creates a logging version of a connection
*
* @param conn - the original connection
* @return - the connection with logging
*/
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
// 使用动态代理的方式创建代理对象
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
} /*
* return the wrapped connection
*
* @return the connection
*/
public Connection getConnection() {
return connection;
} }

其他子类的实现与 ConnectionLogger 类似,不在赘述。

ConnectionLogger 会创建 PreparedStatementLogger 或 StatementLogger,PreparedStatementLogger 会创建 ResultSetLogger,这样就保证了每一步 JDBC 操作在 debug 日志级别下都有日志输出。

那么 ConnectionLogger 又是在哪里创建的呢?跟踪 SQL 的执行流程,在 org.apache.ibatis.executor.BaseExecutor#getConnection 方法中找到 ConnectionLogger 的创建代码:

protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
// 判断日志级别为debug,则创建Connection的代理类ConnectionLogger
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}

从源码中可以看出,如果日志级别为 debug,则会创建代理类 ConnectionLogger,否则只会使用正常的 Connection 对象。

MyBatis 源码篇

MyBatis 源码篇-日志模块2的更多相关文章

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

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

  2. MyBatis 源码篇-插件模块

    本章主要描述 MyBatis 插件模块的原理,从以下两点出发: MyBatis 是如何加载插件配置的? MyBatis 是如何实现用户使用自定义拦截器对 SQL 语句执行过程中的某一点进行拦截的? 示 ...

  3. MyBatis 源码篇-MyBatis-Spring 剖析

    本章通过分析 mybatis-spring-x.x.x.jar Jar 包中的源码,了解 MyBatis 是如何与 Spring 进行集成的. Spring 配置文件 MyBatis 与 Spring ...

  4. MyBatis 源码篇-Transaction

    本章简单介绍一下 MyBatis 的事务模块,这块内容比较简单,主要为后面介绍 mybatis-spring-1.**.jar(MyBatis 与 Spring 集成)中的事务模块做准备. 类图结构 ...

  5. MyBatis 源码篇-DataSource

    本章介绍 MyBatis 提供的数据源模块,为后面与 Spring 集成做铺垫,从以下三点出发: 描述 MyBatis 数据源模块的类图结构: MyBatis 是如何集成第三方数据源组件的: Pool ...

  6. MyBatis 源码篇-资源加载

    本章主要描述 MyBatis 资源加载模块中的 ClassLoaderWrapper 类和 Java 加载配置文件的三种方式. ClassLoaderWrapper 上一章的案例,使用 org.apa ...

  7. MyBatis 源码篇-SQL 执行的流程

    本章通过一个简单的例子,来了解 MyBatis 执行一条 SQL 语句的大致过程是怎样的. 案例代码如下所示: public class MybatisTest { @Test public void ...

  8. MyBatis 源码篇-整体架构

    MyBatis 的整体架构分为三层, 分别是基础支持层.核心处理层和接口层,如下图所示. 基础支持层 反射模块 该模块对 Java 原生的反射进行了良好的封装,提供了更加简洁易用的 API ,方便上层 ...

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

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

随机推荐

  1. vue中书写JSX一些坑-特殊属性名

    举例说明, T1和T2引用Sub时, key2会出现在props以及data.attrs中, 而key则在data中 const Sub = ({data, props}) => { conso ...

  2. Memcached与Memcache区别

    在写这篇文章之前一直对memcache .memcached模糊,相差一个字母,特此总结下: Memcache是什么? Memcache是一个自由和开放源代码.高性能.分配的内存对象缓存系统.用于加速 ...

  3. [转][C#].Net反编译利器

    来自:https://www.cnblogs.com/zsuxiong/p/5117465.html 有以下8款非常不错的.Net反编译利器: 1.Reflector Reflector是最为流行的. ...

  4. python核心模块方法

    ********************os模块: os.remove() 删除文件 os.unlink() 删除文件 os.rename() 重命名文件 os.listdir() 列出指定目录下所有 ...

  5. Qt编写数据可视化大屏界面电子看板11-自定义控件

    一.前言 说到自定义控件,我是感觉特别熟悉的几个字,本人亲自原创的自定义控件超过110个,都是来自各个行业的具体应用真实需求,而不是凭空捏造的,当然有几个小控件也有点凑数的嫌疑,在编写整个数据可视化大 ...

  6. PJzhang:钓鱼域名生成工具urlcrazy

    猫宁!!! www.baidu.com和www.baibu.com是不是很相似,urlcrazy可以自动生成一大批. 这款工具的作者是Andrew Horton 工具下载地址: http://www. ...

  7. LeetCode刷题1——只出现一次的数字

    一.题目要求 二.题目背景 位运算:或,异或,与,移位 三.解题思路 (1)要求算法时间复杂度是线性的,O(n),想到的是先将列表排序,排序后相同的数值两两之间前后相邻,进行偶数次循环,判断两两数值是 ...

  8. 【计算机】DMA原理2

    DMA (直接存储器访问) DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载.否则,C ...

  9. 【VS开发】图像颜色

    版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/lg1259156776/. 最近被图像颜色整的乱七八糟的,一会儿YUV422,一会儿RGB,一会儿gray... ...

  10. 最新 房多多java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.房多多等10家互联网公司的校招Offer,因为某些自身原因最终选择了房多多.6.7月主要是做系统复习.项目复盘.LeetCo ...