Mybatis源码分析二-如何优雅的使用主体日志

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Mybatis源码分析二-如何优雅的使用主体日志

日志模块是mybatis中很重要的一个模块,在我们查询问题和分析sql的时候,经常会将mybatis打印sql的功能打开,但是mybatis并没有给我们提供打印日志的能力,那它是如何使用我们系统支持的日志模块进行日志打印的呢?今天我们来一探究竟。


一.日志模块需求分析


Mybatis的日志模块具备如下特点:


  1. MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,而MyBatis统一提供了trace、debug、warn、error四个级别;
  2. 自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog;
  3. 日志优雅的嵌入到主体功能中,不需要额外改动代码;


二.mybatis日志模块实现分析


2.1.第三方日志组件兼容的实现


从需求上分析,日志模块的第一个需求是一个典型的使用适配器模式的场景。


2.1.1.适配器模式含义


适配器模式(Adapter Pattern) 是作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另外一个接口。适 配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作;类图如下:


角色代表含义:

Target:目标角色,期待 得到的接口.

Adaptee:适配者角色, 被适配的接口.

Adapter:适配器角色, 将源接口转换成目标 接口.


适用场景:当调用双方都不太容易修改的时候,为了复用现有组件可以使用适配器模式;在 系统中接入第三方组件的时候经常被使用到;

PS:如果系统中存在过多的适配器,会增加 系统的复杂性,设计人员应考虑对系统进行重构;

2.1.2.MyBatis 日志模块适配器的使用

mybatis的日志模块的主要实现如下:


20210628203840876.png


  • Target:mybatis中org.apache.ibatis.logging.Log扮演者target角色,用于对内提供统一的日志接口;
  • Adaptee:其他日志组件组件如 slf4J 、commonsLoging 、 Log4J2 等被包含在适配器中,用于被适配。
  • Adapter:针对每个日志组件都提供了适配器, 每 个 适 配 器 都 对 特 定 的 日 志 组 件 进 行 封 装 和 转 换 ,保证对外提供一同的使用标准。; 如 Slf4jLoggerImpl JakartaCommonsLoggingImpl 等;


mybatis的日志模块实现采用适配器模式,日志组件(Target)、适配器以及统一接口(Log 接口) 定义清晰明确符合单一职责原则;同时,客户端在使用日志时,面向 Log 接口编程,不需要 关心底层日志模块的实现,符合依赖倒转原则;最为重要的是,如果需要加入其他第三方日 志框架,只需要扩展新的模块满足新需求,而不需要修改原有代码,这又符合了开闭原则。


2.2.日志组件顺序加载的实现


日志组件的顺序加载实现比较简单,mybatis加载之后在静态代码块中完成的加载。不在过多讲解。


20210912182447679.png


2.3.优雅的使用日志模块的实现


我们在打开mybatis日志的时候,无需任何多余的操作,只需将配置打开即可将看到执行日志。那么我们要实现优雅日志,首先我们得搞清楚,在什么情况下要打日志,通过我们对日志的使用,一般有如下几个位置需要进行日志打印


  1. 在创建 prepareStatement 时,打印执行的 SQL 语句;
  2. 访问数据库时,打印参数的类型和值
  3. 查询出结构后,打印结果数据条数


在mbatis的日志模块中有 BaseJdbcLogger、ConnectionLogger、PreparedStatementLogger 和 ResultSetLogge 通过动态代理负责在不同的位置打印日志;


说到动态代理那么很显然,mybatis的日子增强是通过代理模式进行实现的。那么我们了解下什么是代理模式。


2.3.1.代理模式


代理模式定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用;


20210912184937627.png


例如小王要买个化妆品,但是他不知道渠道,只能通过小张这个海外代购渠道去购买化妆品店。 因此小王不需要和化妆品店直接交互,由小张代理帮他购买即可。

主要目的:

  1. 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的 不必要复杂性;
  1. 通过代理对象对原有的业务增强;

代理模式有静态代理动态代理两种实现方式。

静态代理

这种代理方式需要代理对象和目标对象实现一样的接口。

优点:可以在不修改目标对象的前提下扩展目标对象的功能。

缺点:由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。


动态代理

动态代理利用了 JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。

动态代理又被称为 JDK 代理或接口代理。

静态代理与动态代理的区别主要


1. 静态代理在编译时就已经实现,编译完成后代理类是一个实际的 class 文件


2. 动态代理是在运行时动态生成的,即编译完成后没有实际的 class 文件,而是在运行时 动态生成类字节码,并加载到 JVM 中 注意:动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态 代理。

JDK 中生成代理对象主要涉及两个类,第一个类为 java.lang.reflect.Proxy,通过静态方法 newProxyInstance 生成代理对象,第二个为 java.lang.reflect.InvocationHandler 接口,通过 invoke 方法对业务进行增强;  


2.4.Mybatis日志模块实现分析


2.4.1.基础日志组件


刚才我们讲到mybatis的日志模块中有 BaseJdbcLogger、ConnectionLogger、PreparedStatementLogger 和 ResultSetLogge 通过动态代理负责在不同的位置打印日志;他们的类图如下:


20210912182859789.png

BaseJdbcLogger:所有日志增强的抽象基类,用于记录 JDBC 那些方法需要增强,保存运 行期间 sql 参数信息;


20210912212614633.png


20210912212703346.png

ConnectionLogger:负责打印连接信息和 SQL 语句。通过动态代理,对 connection 进行 增强,如果是调用 prepareStatement、prepareCall、createStatement 的方法,打印要执 行的 sql 语句并返回 prepareStatement 的代理对象(PreparedStatementLogger),让 prepareStatement 也具备日志能力,打印参数;

2021091221275579.png


PreparedStatementLogger:对 prepareStatement 对象增强,增强的点如下:


  • 增强 PreparedStatement 的 setxxx 方法将参数设置到 columnMap、columnNames、 columnValues,为打印参数做好准备;
  • 增强 PreparedStatement 的 execute 相关方法,当方法执行时,通过动态代理打印 参数,返回动态代理能力的 resultSet;
  • 如果是查询,增强 PreparedStatement 的 getResultSet 方法,返回动态代理能力的 resultSet;如果是更新,直接打印影响的行数


2021091221284856.png


ResultSetLogge:负责打印数据结果信息;

20210912212920965.png

2.4.2.日志模块初始化时机


既然在 Mybatis中 Executor 才是访问数据库的组件,日志功能则肯定是在 Executor中被嵌入的, 具体代码在 org.apache.ibatis.executor.SimpleExecutor.prepareStatement(StatementHandler, Log) 方法中,如下图所示:  


20210912212452265.png

学习Mybatis源码是学习设计模式最好的方式,对于以后业务开发模块设计有很大的帮助~

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
开发框架 前端开发 .NET
Abp源码分析之Serilog日志
本文介绍了如何在ASP.NET Core MVC项目和ABP框架中配置和使用Serilog日志库。通过修改`Program.cs`文件,配置日志级别、输出目标,并在控制器和页面模型中记录日志。具体步骤包括新建MVC项目、配置日志、修改控制器和首页代码。最终,日志将被记录到控制台和`Logs/logs.txt`文件中。
38 1
Abp源码分析之Serilog日志
|
2月前
|
Java 数据库连接 mybatis
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
该文档详细介绍了如何在Springboot Web项目中整合Mybatis,包括添加依赖、使用`@MapperScan`注解配置包扫描路径等步骤。若未使用`@MapperScan`,系统会自动扫描加了`@Mapper`注解的接口;若使用了`@MapperScan`,则按指定路径扫描。文档还深入分析了相关源码,解释了不同情况下的扫描逻辑与优先级,帮助理解Mybatis在Springboot项目中的自动配置机制。
162 0
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
|
4月前
|
SQL Java 关系型数据库
SpringBoot 系列之 MyBatis输出SQL日志
这篇文章介绍了如何在SpringBoot项目中通过MyBatis配置输出SQL日志,具体方法是在`application.yml`或`application.properties`中设置MyBatis的日志实现为`org.apache.ibatis.logging.stdout.StdOutImpl`来直接在控制台打印SQL日志。
SpringBoot 系列之 MyBatis输出SQL日志
|
4月前
|
Java 数据库连接 数据库
后端框架的学习----mybatis框架(6、日志)
这篇文章介绍了如何在MyBatis框架中使用日志功能,包括配置MyBatis的日志实现、使用log4j作为日志工具,以及如何通过配置文件控制日志级别和输出格式。
|
5月前
|
SQL 监控 Java
IDEA插件-Mybatis Log Free日志替换
MyBatis Log Free 是一个免费的用于在 IntelliJ IDEA 中显示 MyBatis 日志的插件。它可以帮助您更方便地查看和分析 MyBatis 的 SQL 执行情况,以及定位潜在的性能问题,提高开发效率。
421 0
IDEA插件-Mybatis Log Free日志替换
|
5月前
|
XML Java 数据格式
支付系统----微信支付20---创建案例项目--集成Mybatis-plus的补充,target下只有接口的编译文件,xml文件了,添加日志的写法
支付系统----微信支付20---创建案例项目--集成Mybatis-plus的补充,target下只有接口的编译文件,xml文件了,添加日志的写法
|
6月前
|
SQL Java 数据库连接
IDEA插件(MyBatis Log Free)
IDEA插件(MyBatis Log Free)
397 0
|
6月前
|
SQL Java 数据库连接
Mybatis日志SQL解析
Mybatis日志SQL解析
|
2月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
145 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
2月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
73 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块