MyBatis 为什么可以兼容所有日志框架?

人走茶凉 提交于 2020-11-21 11:09:30

点击上方蓝色字体,选择“标星公众号”


   
   

优质文章,第一时间送达



   
   

关注公众号后台回复paymall获取实战项目资料+视频

作者:可乐
来源:blog.csdn.net/zwx900102/article/details/109025846

前言

日志,在我们开发中是一个非常重要的话题,良好的日志打印可以帮助我们快速的定位问题,可能现在我们开发用到最多的日志框架就是slf4j了,但是日志还有其他很多优秀的框架,比如:Apache Common Log,Log4j,java.util.logging等。
MyBatis作为一款优秀的ORM框架,定义了一套统一的日志接口供应用层调用,而底层却利用适配器模式兼容了我们上面所列出来的常用日志框架。

MyBatis日志分类

在介绍MyBatis的全局配置文件的时候,我们提到setting内有一个属性logImpl,可以配置的选项有:SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING。
这就说明MyBatis支持六种日志类型(NO_LOGGING是不打印日志)。我们看一下MyBatis的日志模块也可以很明显的看出六种日志类型:


它们的对应关系为:
PS:需要注意的是,SLF4J并不是一个具体的日志框架,也就是我们不能单独只配置SLF4J而不引入其他任何具体的日志框架。

简单谈谈SLF4J

SLF4J:简单日记门面。(英文全称为simple logging Facade for Java),这个是用来为各种日志框架提供一个简单的统一的接口,这样使得我们在切换日志框架的时候可以直接替换jar包就可以了,而无需修改源代码。
logback我想大家都用过,logback是一个实现了具体日志打印的框架,但是MyBatis上面列出来的分类并没有支持logback,它又为什么能够打印呢?这就是SLF4J的作用了,因为logback也实现了SLF4J提供的接口,所以我们需要将logback和SLF4J结合配置使用才行。而后面的介绍中也可以看到,MyBatis中如果我们不指定日志种类的时候,优先选择的就是SLF4J,这正是因为SLF4J可以和其他许多日志框架一起结合来使用。
那么假如我们指定了日志类型为SLF4J,但是不引入其他任何实现呢?
答案就是MyBatis不会打印任何日志出来,下图就是只配置了SLF4J而没有引入其他任何实现的警告信息:


可以看到这里提示我们SLF4J没有任何实现,而后面的sql语句和参数这些信息也没有打印出来。

MyBatis日志实现原理

日志的解析

老规矩,我们还是先找到加载mybatis-config配置文件中的解析日志的源码:


这里首先会根据我们配置的属性作为别名去TypeAliasRegistry类中查找对应的类,如果不存在这个别名,那就会把我们配置的属性直接通过Class.forName去查找日志类,所以看到这里就明白我们可以自定义日志类,只要实现Log接口就行,然后配置我们自己的类名就行了。
虽然别名都存在TypeAliasRegistry类里面,但是我们前面介绍MyBatis配置文件的时候,列出了TypeAliasRegistry类中默认初始化的别名,并没有看到日志相关类的别名,那么日志的别名又是在哪里配置的呢?我们打开Configuration类:
可以看到Configuration的构造方法里面也初始化了一些别名注册到TypeAliasRegistry类了。
接下来我们看看读取到日志类之后调用了setLogImpl做了什么事情:

调用了LogFactory类的方法。

LogFactory

LogFactory 工厂 是负责创建日志对象对应的适配器。
LogFactory的静态代码块内按顺序初始化了所有内置的日志
再看一下tryImplementation方法,如果logConstructor不为空,说明当前还没有加载到日志适配器,那就继续执行run()方法,也就是继续执行useXXXLogging方法,而所有的useXXXLogging方法都是调用了setImplementation方法。
下面这里如果加载成功之后就会对logConstructor进行赋值,那么后续的方法就不会再执行run()方法, 而如果抛出异常,因为已经被捕获了,所以就会继续往后执行静态代码块内的方法。
从上面的LogFactory中我们可以看到,初始化的时候就会默认初始化一个日志适配器,所以如果我们引用了相关日志所需要的类,那么就会按照static代码块内的顺序进行选择一个合适的日志适配器。
继续回到上面的Configuration里面,这里拿到我们配置的日志信息之后,会直接调用useCustomLogging方法,也就是绕过了上面的logConstructor == null这个判断,而直接调用了setImplementation方法,所以假如我们配置了日志信息,那么会覆盖初始化的日志适配器。
PS:假如我们配置了一个不存在的日志类,那么调用setImplementation方法的时候异常就会被抛出来,因为捕获异常的方法是在tryImplementation而不是在setImplementation。

jdbc log

MyBatis的日志包下面还有一个包时jdbc,这个我们还没有介绍,那么jdbc包下面的类又有什么用呢?我们先看一下类图关系:
很明显,MyBatis将日志拆分成了ConnectionLogger,PreparedStatementLogger,ResultSetLogger,StatementLogger四种类型分开处理,它们都继承了BaseJdbcLogger类,而且实现了InvocationHandler接口,也很明显,这里用到了JDK动态代理。
任意点开ConnectionLogger可以发现,它是用来代理Connection对象的:

其他三个那很明显,分别是用来代理PreparedStatement,ResultSet,Statement这三个对象的。也就是说MyBatis中日志最终的打印是通过JDK动态代理来实现的,而且不同的执行过程分成了四个对象来分别负责对应的日志打印。
我们继续看一下ConnectionLogger的invoke方法,可以看到,这里就是打印了一句日志:
上面日志打印出来的效果就是我们下面红框中的日志:

等等,差点被忽悠了,这代码里面并没有打印“==>”,打印出来的这个符号又是怎么来的呢?
那就需要进入debug方法里面继续看一看,这个debug方法是在抽象类BaseJdbcLogger里面实现的,所以我们还需要看看BaseJdbcLogger类的debug方法。
可以看到这里打印的时候拼接了一个前缀:

PS:queryStack是查询层数,如果没有嵌套查询则queryStack=1

总结

本文主要分析了MyBatis日志的加载原理,并对LogFactory作为适配器对象工厂是如何选择日志适配器对象进行了分析。最后分析了MyBatis是如何通过动态代理将不同日志类型分为不同对象来实现日志打印功能的。

有热门推荐👇

从此抛弃理try...catch!

Spring Security 真正的前后分离实现
Docker安装Jenkins+Shell脚本自动化部署项目
Mybatis 使用的 9 种设计模式,真是太有用了

Java数组转List的三种方式及对比

推荐一款日志切割神器!我常用~

使用 Stream API 高逼格 优化 Java 代码!

Spring MVC请求处理过程不是两张流程图就能讲清楚的

撸一个简易聊天室,不信你学不会实时消息推送(附源码)

最后分享一套微服务电商项目教程(资料笔记+视频):点击阅读全文获取面试资料+项目实战资料(电商/聚合支付)

SPringCloud微服电商完整务教程

1.框架搭建
- 电商项目介绍
- 微服务环境搭建
- 数据库搭建

2.分布式存储系统
- FastDFS原理讲解
- 文件上传
- 文件下载
3.商品发布
- 表结构梳理
- 代码生成器的使用
- 商品增删改
- 商品查询
4.lua,canal实现广告缓存
- 首页广告表设计
- Lua安装使用讲解
- Nginx限流实战
- Canal安装,原理介绍
- Canal同步数据实现
5.索引搜索
- ES安装讲解
- Kibana安装讲解
- DSL语句
- ES API使用
6.商品搜索
- ES 高级搜索功能
- ES 排序规则

 7.Thymeleaf实现静态页面
- Thymeleat 缓存配置讲解
- 搜索页面讲解
8.微服务网关和Jwt令牌
- 微服务网关Zuul/Gateway介绍
- 网关之负载和限流
- 用户服务搭建
- JWT token讲解
- 网关鉴权
9.Spring Security Oauth2
- 单点登陆介绍
- Oauth2介绍
- 共钥私钥讲解
- 加密算法讲解
10.购物车
- 购物车分析和购物车种类分析
- 订单服务创建
- 购物车功能实现
11.订单
- 用户地址测试
- 下单问题分析,幂等
- 用户积分规则
- 二维码生产讲解
- 微信支付流程及模式讲解
12.微信支付
- 微信支付SDK使用讲解
- 微信支付状态查询
- 内网穿透 花生壳
- 微信支付回调
- rabbitMQ 延时队列讲解
13.秒杀基础
- 秒杀需求分析
- 秒杀服务搭建
- 秒杀之Redis
- 秒杀之多线程
14.秒杀核心
- 重复抢单下单问题
- 超卖问题
- 秒杀支付
15.分布式事物
- 分布式事物介绍
- CAP理论介绍
- 2pc/3pc 机制讲解
- TCC事物补偿
- Seata案列讲解
16.高可用集群
- 分布式和集群概念
- Eureka集群介绍
- Redis 集群介绍
- RabbitMq集群安装

点击阅读原文,前往上面微服务电商教程文档

本文分享自微信公众号 - java版web项目(java_project)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!