当前位置: 首页 > news >正文

Mybatis日志模块分析--适配器模式+代理模式

适配器模式

日志在我们开发过程中占据了一个非常重要的地位,是开发和运维管理之间的桥梁,在Java中的日志框架也非常多,Log4j,Log4j2,Apache Commons Log,java.util.logging,slf4j等,这些工具对外的接口也都不尽相同,为了统一这些工具,MyBatis定义了一套统一的日志接口供上层使用。

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

LogFactory工厂类负责创建日志组件适配器

在LogFactory类加载时会执行其静态代码块,其逻辑是按序加载并实例化对应日志组件的适配器,然后使用LogFactory.logConstructor这个静态字段,记录当前使用的第三方日志组件的适配器。

日志应用

首先我们在全局配置文件中我们可以设置对应的日志类型选择

在Configuration的构造过程中实现类型别名的注册,之后再解析配置文件

JDBC日志打印

MyBatis中的日志模块中包含了一个jdbc包,它并不是将日志信息通过jdbc操作保存到数据库中,而是通过JDK动态代理的方式,将JDBC操作通过指定的日志框架打印出来。

BaseJdbcLogger

  // 4个实现都实现了InvocationHandler接口
  // 记录 PreparedStatement 接口中定义的常用的set*() 方法
  protected static final Set<String> SET_METHODS;
  // 记录了 Statement 接口和 PreparedStatement 接口中与执行SQL语句有关的方法
  protected static final Set<String> EXECUTE_METHODS = new HashSet<>();

  // 记录了PreparedStatement.set*() 方法设置的键值对
  private final Map<Object, Object> columnMap = new HashMap<>();
  // 记录了PreparedStatement.set*() 方法设置的键 key
  private final List<Object> columnNames = new ArrayList<>();
  // 记录了PreparedStatement.set*() 方法设置的值 Value
  private final List<Object> columnValues = new ArrayList<>();

  protected final Log statementLog;// 用于日志输出的Log对象
  protected final int queryStack;  // 记录了SQL的层数,用于格式化输出SQL

ConnectionLogger

ConnectionLogger的作用是记录数据库连接相关的日志信息,在实现中是创建了一个Connection的代理对象,在每次Connection操作的前后我们都可以实现日志的操作。

应用实现

在实际处理的时候,日志模块是如何工作的,我们来看看。

在我们要执行SQL语句前需要获取Statement对象,而Statement对象是通过Connection获取的,所以我们在SimpleExecutor中就可以看到相关的代码

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    // 获取 Statement 对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 为 Statement 设置参数
    handler.parameterize(stmt);
    return stmt;
  }

先进入如到getConnection方法中

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      // 创建Connection的日志代理对象
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

在进入到handler.prepare方法中

  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        // 在执行 prepareStatement 方法的时候会进入进入到ConnectionLogger的invoker方法中
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

在执行sql语句的时候

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 到了JDBC的流程
    ps.execute(); // 本质上 ps 也是 日志代理对象
    // 处理结果集
    return resultSetHandler.handleResultSets(ps);
  }

如果是查询操作,后面的ResultSet结果集操作,其他是也通过ResultSetLogger来处理的,同理啦!

相关文章:

  • 身份验证:区块链如何让用户掌控一切
  • Scrapy对比Selenium:哪个最适合您的网络爬虫项目
  • 深度学习Note.5(机器学习2)
  • Unity中UDP异步通信常用API使用
  • Python小练习系列 Vol.7:全排列生成(回溯算法模板题)
  • Spring笔记03-依赖注入
  • Javaweb后端登录认证 登录校验 过滤器 filter令牌校验,执行流程,拦截路径
  • 业之峰与宏图智能战略携手,开启家装数字化新篇章
  • excel 时间戳 转日期
  • shop搜索需求及测试点
  • 01-Docker 安装
  • 知识图谱之知识抽取:从数据海洋中 “捞金”
  • Plastiform复制胶泥:高精度表面复制与测量的高效工具
  • 如何学习Python编程?
  • Emacs 折腾日记(二十)——修改emacs的一些默认行为
  • 三层交换综合实验
  • 【Java SE】StringBuffer、StringBuilder详解
  • 【C++】C++11介绍列表初始化右值引用和移动语义
  • .js项目编译成.exe程序(交叉编译全过程整理)
  • 在 Ant Design Vue 中实现滚动页面时保持下拉菜单展开
  • 人物|德国新外长关键词:总理忠实盟友、外交防务专家、大西洋主义者
  • 徐徕任上海浦东新区副区长,此前已任区委常委
  • 节前A股持续震荡,“五一”假期持股还是持币过节胜率更高?
  • 五一“拼假”催热超长假期,热门酒店民宿一房难求
  • 企业取消“大小周”引热议,半月谈:不能将显性加班变为隐性加班
  • 商务部:一季度我国服务贸易较快增长,进出口总额同比增8.7%