Mybatis源码分析,动态代理 增强 debug补充示例图

debug补充示例图


起始debug源码:
首先搞清楚那些地方需要打印日志?通过对日志的观察,如下几个位置需要打日志:

  1. 在创建prepareStatement 时,打印执行的 SQL 语句;
  2. 访问数据库时,打印参数的类型和值
  3. 查询出结构后,打印结果数据条数
    因此在日志模块中有 BaseJdbcLogger 、ConnectionLogger 、PreparedStatementLogger
    ResultSetLogge 通过动态代理负责在不同的位置打印日志;几个相关类的类图如下:
    在这里插入图片描述
     BaseJdbcLogger:所有日志增强的抽象基类,用于记录 JDBC 那些方法需要增强,保存运
    行期间sql 参数信息;
     ConnectionLogger:负责打印连接信息和 SQL 语句。通过动态代理,对 connection 进行
    增强,如果是调用 prepareStatement、prepareCall、createStatement 的方法,打印要执
    行的 sql 语句并返回 prepareStatement 的代理对象(PreparedStatementLogger),让
    prepareStatement 也具备日志能力,打印参数;
     PreparedStatementLogger:对prepareStatement 对象增强,增强的点如下:
     增强 PreparedStatement 的 setxxx 方法将参数设置到 columnMap、columnNames、
    columnValues,为打印参数做好准备;
     增强 PreparedStatement 的 execute 相关方法
    参数,返回动态代理能力的resultSet;
     如果是查询,增强 PreparedStatement
    resultSet;如果是更新,直接打印影响的行数
    ResultSetLogge:负责打印数据结果信息;
    最后一个问题:上面讲这么多,都是日志功能的实现,那日志功能是怎么加入主体功能的?
    答:既然在Mybatis 中Executor 才是访问数据库的组件,日志功能是在Executor 中被嵌入的,
    具体代码在org.apache.ibatis.executor.SimpleExecutor.prepareStatement(StatementHandler, Log)
    方法中,如下图所示:
 //创建Statement
 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
   Statement stmt;
   //获取connection对象的动态代理,添加日志能力;
   Connection connection = getConnection(statementLog);
   //通过不同的StatementHandler,利用connection创建(prepare)Statement
   stmt = handler.prepare(connection, transaction.getTimeout());
   //使用parameterHandler处理占位符
   handler.parameterize(stmt);
   return stmt;
 } 
 //重点关注这个方法: 
   protected Connection getConnection(Log statementLog) throws SQLException {
   Connection connection = transaction.getConnection();
   if (statementLog.isDebugEnabled()) {
       //通过动态代理为我们的对象提供打印日志的功能
     return ConnectionLogger.newInstance(connection, statementLog, queryStack);
   } else {
     return connection;
   }
 }

ConnectionLogger.newInstance(connection, statementLog, queryStack);
这个方法也就是调用ConnectionLogger 的invoke方法

@Override
//对连接的增强
public Object invoke(Object proxy, Method method, Object[] params)
    throws Throwable {
  try {
  	//如果是从Obeject继承的方法直接忽略
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, params);
    }
    //如果是调用prepareStatement、prepareCall、createStatement的方法,打印要执行的sql语句
    //并返回prepareStatement的代理对象,让prepareStatement也具备日志能力,打印参数
      //生成具有打印功能的
    if ("prepareStatement".equals(method.getName())) {
      if (isDebugEnabled()) {
        debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);//打印sql语句
      }        
      PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
      stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);//创建代理对象
      return stmt;
    } else if ("prepareCall".equals(method.getName())) {
      if (isDebugEnabled()) {
        debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);//打印sql语句
      }        
      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);
  }
}

**这个方法中关注 PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);这个方法,开头已经对它做了介绍 这个也是框架自带的方法它里面有一个代理方法 **

 //1,增强PreparedStatement的setxxx方法将参数设置到columnMap、columnNames、columnValues,为打印参数做好准备
   //2. 增强PreparedStatement的execute相关方法,当方法执行时,通过动态代理打印参数,返回动态代理能力的resultSet
   //3. 如果是查询,增强PreparedStatement的getResultSet方法,返回动态代理能力的resultSet
   //   如果是更新,直接打印影响的行数
   @Override
   public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
       try {
           if (Object.class.equals(method.getDeclaringClass())) {
               return method.invoke(this, params);
           }
           //增强PreparedStatement的execute相关方法
           if (EXECUTE_METHODS.contains(method.getName())) {
               if (isDebugEnabled()) {
                   //当方法执行时,通过动态代理打印参数
                   debug("Parameters: " + getParameterValueString(), true);
               }
               clearColumnInfo();
               //返回动态代理能力的resultSet
               if ("executeQuery".equals(method.getName())) {
                   ResultSet rs = (ResultSet) method.invoke(statement, params);
                   return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
               } else {
                   return method.invoke(statement, params);
               }
               //将参数设置到columnMap、columnNames、columnValues,为打印参数做好准备
           } else if (SET_METHODS.contains(method.getName())) {
               if ("setNull".equals(method.getName())) {
                   setColumn(params[0], null);
               } else {
                   setColumn(params[0], params[1]);
               }
               return method.invoke(statement, params);
               //返回动态代理能力的resultSet
           } else if ("getResultSet".equals(method.getName())) {
               ResultSet rs = (ResultSet) method.invoke(statement, params);
               return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
               //   如果是更新,直接打印影响的行数
           } else if ("getUpdateCount".equals(method.getName())) {
               int updateCount = (Integer) method.invoke(statement, params);
               if (updateCount != -1) {
                   debug("   Updates: " + updateCount, false);
               }
               return updateCount;
           } else {
               return method.invoke(statement, params);
           }
       } catch (Throwable t) {
           throw ExceptionUtil.unwrapThrowable(t);
       }
   }

关注 return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);这个方法

 @Override
  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }
        //执行result.next方法,判断是否还有数据
      Object o = method.invoke(rs, params);
        //如果还有数据,计数器rows加一
      if ("next".equals(method.getName())) {
        if (((Boolean) o)) {
          rows++;
          if (isTraceEnabled()) {
            ResultSetMetaData rsmd = rs.getMetaData();
            final int columnCount = rsmd.getColumnCount();
            if (first) {
              first = false;
              printColumnHeaders(rsmd, columnCount);
            }
            printColumnValues(columnCount);
          }
        } else {
            //如果没有数据了,打印rows,打印查询出来的数据条数
          debug("     Total: " + rows, false);
        }
      }
      clearColumnInfo();
      return o;
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

如图:
在这里插入图片描述
mybatis就是通过有这几个代理方法来增强对jdbc的方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值