前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mybatis打印JDBC日志源码分析(JDK动态代理)

mybatis打印JDBC日志源码分析(JDK动态代理)

原创
作者头像
猿码YM
发布2021-11-30 15:34:59
6390
发布2021-11-30 15:34:59
举报
文章被收录于专栏:mybatis探究

mybatis支持不同的数据库,具体访问数据库是由不同的数据库厂商提供的驱动jar包。

比如:mysql的mysql-connector-java.jar

不同厂商提供的提供的jar也是基于JDK下的java.sql包中的接口去实现的

比如:不同的数据库厂商基于Connection接口实现了自家的Connection访问数据库类

不同厂商只要基于JDK提供的接口实现类,就能访问数据库

此处不得不赞美面向接口编程的精妙之处

mybatis框架的作用是把数据库的元数据转成java对象,方便java代码操作,具体的数据库访问操作由JDBC完成。但是很多时候我们想要看到mybaits和jdbc之间的交互痕迹,比如:mybatis调用了JDBC的哪些方法,传了什么参数,得到了什么执行结果。

如果打出这些sql日志方便我们bug排查和分析,也可以根据sql优化代码,比如:通过优化mybatis的缓存使用,减少数据访问次数,减少数据库压力,提高系统响应时间。

  1. 实现JDBC sql日志打印的类:
    在这里插入图片描述
    在这里插入图片描述
  2. 类的继承关系:
    在这里插入图片描述
    在这里插入图片描述

connection打印日志的代理类ConnectionLogger,这个命名有点误导,看命名不会把它当作是一个proxy,但是看到他实现了InvocationHandler接口就知道这是一个代理类了。

代理类持有一个connection接口,并不是具体的类,具体实现类看jdbc接入的是什么数据库的connection实现类,再次体现了面向接口编程的精妙之处😂,也体现了java多态的特性。

  1. 下面看ConnectionLogger源码注释:
代码语言:txt
复制
/**
 * Connection proxy to add logging
 * BaseJdbcLogger实现了几个代理的共同方法
 */
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 {
      // 返回拥有method方法的class
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }
      /**
       * jdbc包下的这些类相当于装饰器
       * 不过这里从装饰是把返回的类都装饰成了代理类
       * 判断当前的方法是否需要代理
       */
      if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
        }
        // 通过反射的方式调用代理类的代理方法
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        // PreparedStatement对返回值再次加工和封装,返回一个新的代理类
        // 代理和被代理的类都需要实现同样的接口,这样就可以发挥java多态的特性了
        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.
   * 
   * 创建Connection代理的方法,注意这是一个static方法
   */
  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);
  }
}

此段源码可以作为JDK动态代理和java面向接口编程的典型场景进行分析

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档