Mybatis基础模块-日志管理

文章目录

  • 1. 适配器模式
  • 2. Log
    • 2.1 默认实现StdOutImpl
    • 2.2 Log4jImpl
  • 3. LogFactory
  • 4. 解析配置和应用
    • 4.1 settings配置
    • 4.2 解析
  • 5. jdbc日志
    • 5. 1 类图
    • 5.2 BaseJdbcLogger
    • 5.3 ConnectionLogger
    • 5.4 ConnectionLogger的具体应用

1. 适配器模式

适配器使接口不兼容的对象可以相互合作。
在这里插入图片描述
Java的日志框架有很多,Log4j,Log4j2,Apache Commons Log,java.util.logging,slf4j等,接口不尽相同,Mybatis为了统一匹配这些框架,使用到了适配器模式。

2. Log

Mybatis自定义了日志接口:org.apache.ibatis.logging.Log。

/*** @author Clinton Begin*/
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);}

以下为其类图:
在这里插入图片描述
上图的实现相当于adapter。

2.1 默认实现StdOutImpl

public class StdOutImpl implements Log {public StdOutImpl(String clazz) {// Do Nothing}@Overridepublic boolean isDebugEnabled() {return true;}@Overridepublic boolean isTraceEnabled() {return true;}@Overridepublic void error(String s, Throwable e) {System.err.println(s);e.printStackTrace(System.err);}@Overridepublic void error(String s) {System.err.println(s);}@Overridepublic void debug(String s) {System.out.println(s);}@Overridepublic void trace(String s) {System.out.println(s);}@Overridepublic void warn(String s) {System.out.println(s);}
}

这个实现很简单,就是控制台把日志打印出来。

2.2 Log4jImpl

为了匹配Log4j,该类持有org.apache.log4j.Logger,Logger就是上图adaptee,被匹配的接口。

/*** @author Eduardo Macarron*/
public class Log4jImpl implements Log {private static final String FQCN = Log4jImpl.class.getName();private final Logger log;public Log4jImpl(String clazz) {log = Logger.getLogger(clazz);}@Overridepublic boolean isDebugEnabled() {return log.isDebugEnabled();}@Overridepublic boolean isTraceEnabled() {return log.isTraceEnabled();}@Overridepublic void error(String s, Throwable e) {log.log(FQCN, Level.ERROR, s, e);}@Overridepublic void error(String s) {log.log(FQCN, Level.ERROR, s, null);}@Overridepublic void debug(String s) {log.log(FQCN, Level.DEBUG, s, null);}@Overridepublic void trace(String s) {log.log(FQCN, Level.TRACE, s, null);}@Overridepublic void warn(String s) {log.log(FQCN, Level.WARN, s, null);}}

3. LogFactory

LogFactory使用工厂模式,创建各类适配器。 该类用final修饰,不可继承,同时构造方法是私有的,不能通过new方法创建,像是一个工具类。


/*** @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(LogFactory::useSlf4jLogging); 等价于* tryImplementation(new Runnable(){*   void run(){*     useSlf4jLogging();*   }* })*/tryImplementation(LogFactory::useSlf4jLogging);tryImplementation(LogFactory::useCommonsLogging);tryImplementation(LogFactory::useLog4J2Logging);tryImplementation(LogFactory::useLog4JLogging);tryImplementation(LogFactory::useJdkLogging);tryImplementation(LogFactory::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 字段logConstructor = candidate;} catch (Throwable t) {throw new LogException("Error setting Log implementation.  Cause: " + t, t);}}}

4. 解析配置和应用

问题:mybatis是如何选择日志框架的。

4.1 settings配置

在这里插入图片描述
配置值包括:SLF4J , LOG4J(deprecated since 3.5.9) , LOG4J2 , JDK_LOGGING |,COMMONS_LOGGING , STDOUT_LOGGING , NO_LOGGING。

配置的值是如何来的?
在配置对象Configuration的构造方法里面。
在这里插入图片描述

4.2 解析

XMLConfigBuilder的方法:loadCustomLogImpl().

private void loadCustomLogImpl(Properties props) {// 获取 logImpl设置的 日志 类型Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));// 设置日志  这块代码是我们后面分析 日志 模块的 关键代码configuration.setLogImpl(logImpl);}

继续:

public void setLogImpl(Class<? extends Log> logImpl) {if (logImpl != null) {this.logImpl = logImpl; // 记录日志的类型// 设置 适配选择LogFactory.useCustomLogging(this.logImpl);}}

继续:

public static synchronized void useCustomLogging(Class<? extends Log> clazz) {setImplementation(clazz);}

继续:

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 字段logConstructor = candidate;} catch (Throwable t) {throw new LogException("Error setting Log implementation.  Cause: " + t, t);}}

5. jdbc日志

有了以上的类,那mybatis是如何打印日志的?

Mybatis通过JDK动态代理的方式,将JDBC操作通过指定的日志框架打印出来。

5. 1 类图

BaseJdbcLogger

5.2 BaseJdbcLogger

BaseJdbcLogger是一个基础的抽象类。基本的属性:

// 记录 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*() 方法设置的键 keyprivate final List<Object> columnNames = new ArrayList<>();// 记录了PreparedStatement.set*() 方法设置的值 Valueprivate final List<Object> columnValues = new ArrayList<>();protected final Log statementLog;// 用于日志输出的Log对象protected final int queryStack;  // 记录了SQL的层数,用于格式化输出SQL

5.3 ConnectionLogger


/*** Connection proxy to add logging.** @author Clinton Begin* @author Eduardo Macarron**/
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {// 真正的Connection对象private final Connection connection;private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {super(statementLog, queryStack);this.connection = conn;}/*** Connection 是一个数据库连接对象*            通过 Connection 的下一步是创建 Statement 对象*            Statement包含对应的子类 PreparedStatement**            日志记录  Connection 创建 Statement 的过程*            同时会创建 Statement 的代理对象类增强 Statement** @param proxy* @param method* @param params* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] params)throws Throwable {try {// 如果是调用从Object继承过来的方法,就直接调用 toString,hashCode,equals等if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, params);}// 如果调用的是 prepareStatement方法if ("prepareStatement".equals(method.getName())) {if (isDebugEnabled()) {debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);}// 创建  PreparedStatementPreparedStatement 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();// 创建了 Connection的 代理对象 目的是 增强 Connection对象 给他添加了日志功能return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);}/*** return the wrapped connection.** @return the connection*/public Connection getConnection() {return connection;}}

5.4 ConnectionLogger的具体应用

在实际处理的时候,日志模块是如何工作的?

  • 执行sql之前先要获取Statement.
    SimpleExecutor的方法doQuery
/*** 到了 具体的数据库操作的步骤了  JDBC*     Connection*     Statement*     PreparedStatement* @param ms* @param parameter* @param rowBounds* @param resultHandler* @param boundSql* @param <E>* @return* @throws SQLException*/@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 注意,已经来到SQL处理的关键对象 StatementHandler >>  同时会完成  parameterHandler和resultSetHandler的实例化// 默认创建的是 PreparedStatementHandlerStatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 获取一个 Statement对象  对占位符处理stmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 用完就关闭closeStatement(stmt);}}
  • 继续看prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;// 放开了日志 会创建 Connection 的代理对象Connection connection = getConnection(statementLog);// 获取 Statement 对象 ==》 PreparedStatement对象 Statement  如果放开了日志 则会创建 Statement 对应的代理对象stmt = handler.prepare(connection, transaction.getTimeout());// 为 Statement 设置参数  如果是PreparedStatement处理则会对 对应的占位符赋值handler.parameterize(stmt);return stmt;}
  • 继续看getConnection方法,
 protected Connection getConnection(Log statementLog) throws SQLException {// 获取到了真正的 Connection 对象 ? 如果有连接池管理 在此处获取的是PooledConnection 是Connection的代理对象Connection connection = transaction.getConnection();if (statementLog.isDebugEnabled()) {// 创建Connection的日志jdk代理对象return ConnectionLogger.newInstance(connection, statementLog, queryStack);} else {// 返回的是真正的Connection 没有走代理的方式return connection;}}
  • 再看 handler.prepare(connection, transaction.getTimeout()), 进入到instantiateStatement方法
@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {String sql = boundSql.getSql();if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {String[] keyColumnNames = mappedStatement.getKeyColumns();if (keyColumnNames == null) {// connection 是 日志的代理对象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);}}

在这里插入图片描述

  • 执行查询handler.query(stmt, resultHandler);

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA常用高效开发工具—screw一键生成数据库文档(仅需三步)

1.配置 引入screw核心... <!-- screw核心 --> <dependency><groupId>cn.smallbun.screw</groupId><artifactId>screw-core</artifactId><version>1.0.3</version> </dependency><!-- HikariCP --> <dependency…

LLDP协议(链路层链路发现协议)

文章目录 LLDP协议&#xff08;链路层链路发现协议&#xff09;TLVLLDPDULLDP工作机制LLDP 报文ethernet IISNAP LLDP协议&#xff08;链路层链路发现协议&#xff09; 随着网络技术的发展&#xff0c;接入网络的设备的种类越来越多&#xff0c;配置越来越复杂&#xff0c;来自…

1、Linux驱动开发:模块_加载卸载

目录 &#x1f345;点击这里查看所有博文 随着自己工作的进行&#xff0c;接触到的技术栈也越来越多。给我一个很直观的感受就是&#xff0c;某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了&#xff0c;只有经常会用到的东西才有可能真正记…

github上下载的vscode extension报找不到“vscode“模块的问题

问题来自于下载的 vscode 扩展源代码包 import * as vscode from "vscode"; 有问题的是这句&#xff0c;找不到 vscode 模块&#xff0c;错误代码 2307 主要是因为下载的源码工程里&#xff0c;没有 node_modules 这个文件夹&#xff0c;因为它本来就是需要被忽略…

97、Kafka的性能好在什么地方

Kafka的性能好在什么地方 一、顺序写二、零拷贝三、额外补充 kafka不基于内存&#xff0c;而是硬盘存储&#xff0c;因此消息堆积能力更强 一、顺序写 顺序写 : 利用磁盘的顺序访问速度可以接近内存&#xff0c;kafka的消息都是append操作&#xff0c;partition是有序的&#…

(数据结构)哈夫曼编码实现(C语言)

(数据结构)哈夫曼编码实现&#xff08;C语言&#xff09; 哈夫曼的编码:从一堆数组当中取出来最小的两个值&#xff0c;按照左下右大的进行绘制&#xff0c;将两个权值之和&#xff0c;放入队列当中&#xff0c;然后再进行取出两个小的&#xff0c;以此类推&#xff0c;直到全部…

VuePress在生产环境跳转子页报错 Failed to execute ‘appendChild‘ on ‘Node‘

记录一个使用VuePress时遇到的问题 使用VuePress做了一个文档网页&#xff0c;在开发环境的时候一切正常&#xff0c;但是发布到生产环境后&#xff0c;直接跳转二级页面会报错Failed to execute appendChild on Node 比如主页是http://sun/docs/.vuepress/dist/index.html#/…

SwiftUI-基础

应用入口 Main函数与App结构体的绑定&#xff0c;遵循App协议 main struct BaseApp: App {var body: some Scene {WindowGroup {ContentView()}} } 兼容UIApplicationDelegate main struct BasicApp: App {UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate…

【C语言项目】三子棋

文章目录 项目思路一、分文件进行创建二、进入游戏前的目录2.1 目录的功能&#xff1a;2.2 目录界面&#xff1a;2.3 选择进入或退出游戏2.4 多次重玩功能 三、画出棋盘3.1 写出棋子3.2 初始化棋盘3.2 画出棋盘的框架3.3 代码实现 四、玩家落子4.1 落子逻辑4.2具体情况分类讨论…

抖斗音直播间评论引流助手,支持直播间喊话+视频评论区喊话=到指定直播间引流精准粉丝【永久脚本+详细教程】

如果你觉得直播间发言手动太麻烦了&#xff0c;或许这个自动工具能帮到你&#xff01; 1.开始运行前&#xff0c;需要手动去打开打开直播间或者视频评论区&#xff0c;再运行脚本。 2.脚本就是模拟人工操作&#xff0c;在相应的APP里进行评论&#xff0c;无突破APP限制功能。…

[USACO14DEC] Marathon G

洛谷[USACO14DEC] Marathon G 题目大意 Bessie \text{Bessie} Bessie设计了一条马拉松路线&#xff0c;有 N N N个点。 Bessie \text{Bessie} Bessie有 q q q次操作&#xff0c;每次操作是修改或询问。每次修改会修改一个点的坐标&#xff0c;每次询问是选手跑过一条子路径的时…

阿里云ACK的etcd证书过期手工升级操作

一.问题现象 阿里云ACK的etcd证书过期&#xff0c;通过图形化界面升级提示升级失败&#xff0c;考虑通过脚本的方式升级ETCD相关的证书。由于在前期做类似的升级ETCD证书失败导致整个集群业务出现访问异常&#xff0c;所有在升级之前做好对应的备份操作是很有必要的 二.前期准…

【Kubernetes运维篇】ingress-nginx实现业务灰度发布详解

文章目录 一、理论&#xff1a;实现灰度发布的几种场景1、场景一&#xff1a;将新版本灰度给部分用户2、场景二&#xff1a;按照比例流程给新版本3、实现灰度发布字段解释 二、实践&#xff1a;1、实验前提环境2、基于Request Header(请求头)进行流量分割3、基于Cookie进行流量…

93.qt qml-自定义Table优化(新增:水平拖拽/缩放自适应/选择使能/自定义委托)

之前我们更新了90.qt qml-Table表格组件(支持表头表尾固定/自定义颜色/自定义操作按钮/排序)_qml 表格_诺谦的博客-CSDN博客 但是一直没出源码,是因为该demo还存在问题,那就是表头表尾固定下,如果是半透明状态下,会看到表头表尾固定后的内容,所以只能重构代码,不能使用重…

Vue3组合式API+TypeScript写法入门

文章目录 前言1.reactive2.ref3.props4.computed5.emit6.watch总结 前言 参考Vue3官网. 本篇以组合式API为例, 但不包含setup语法糖式写法. 原本打算结合class-component, Vue3不推荐就不用了: OverView|Vue Class Component. 而且是不再推荐基于类的组件写法, 推荐单文件组件…

Android App 持续集成性能测试:启动流量

目录 前言&#xff1a; get app UID 获取流量数据 获得启动流量数据 总结 前言&#xff1a; Jenkins 是一种开源的持续集成工具&#xff0c;可以帮助我们更加方便地进行软件开发和测试工作。通过 API 远程管理 Jenkins 可以帮助我们更加方便地进行 Jenkins 的配置和管理工…

react实现路由跳转动画

下载插件 npm i react-transition-group 配置路由 import { createBrowserRouter as ReactRouter,Navigate } from "react-router-dom";import App from ../App.js import Login from "../view/login.js"; import Home from "../home.js"; co…

了解 3DS MAX 3D摄像机跟踪设置:第 4 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 转到合成>新合成以创建新合成。 将“宽度”和“高度”值分别设置为 1280 和 720。将帧速率设置为 25&#xff0c;将持续时间设置为 12 秒。单…

Go基本数据类型及内置函数(一文稳定基础)

文章目录 1. 基础数据类型2.内置函数3.函数4.列表操作及for循环5.原子性操作6.通道7.协程与并发安全8.定时器 1. 基础数据类型 1. bool&#xff1a;布尔类型&#xff0c;只有 true 和 false 两个值。var test2 bool true2. string&#xff1a;字符串类型&#xff0c;表示一组字…

微服务网关

1.网关是如何演化来的&#xff0c;在微服务中有什么作用&#xff1f; 随着单体架构转化为微服务架构的时候&#xff0c;由一个后台服务由一个单一的服务变成了多个微服务&#xff0c;前端应用需要调用多个服务的接口&#xff0c;为了解决这个问题&#xff0c;网关就产生了。网…