点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

作者 | 双子孤狼

来源 | https://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的日志模块也可以很明显的看出六种日志类型:它们的对应关系为:

日志属性 对应日志模块包名 实现方式
SLF4J slf4j 使用SLF4J日志框架实现
LOG4J log4j 使用Log4J日志框架实现(1.x版本)
LOG4J2 log4j2 使用Log4J日志框架实现(2.x版本)
JDK_LOGGING jdk14 使用java.util.logging实现
COMMONS_LOGGING commons 使用Apache Commons Logging实现
STDOUT_LOGGING stdout 使用System类实现
NO_LOGGING nologging 不打印日志

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是如何通过动态代理将不同日志类型分为不同对象来实现日志打印功能的。

DD自研的沪牌代拍业务,点击直达

【往期推荐】

读完《Effective Java》后,总结了 50 条开发技巧

2020-11-20

35岁之后,你还会继续写代码吗?

2020-11-19

11月全国招程序员34万人,猜猜平均工资是多少?

2020-11-18

一个复杂系统的拆分改造,压力真大!

2020-11-19

双十一,你买了哪些大呼过瘾的编程书?

2020-11-17

扫一扫,关注我

一起学习,一起进步

每周赠书,福利不断

深度内容

推荐加入

MyBatis日志到底是如何做到兼容所有常用日志框架的?相关推荐

  1. 萌新发问:MyBatis日志到底是如何做到兼容所有常用日志框架的?

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:推荐 19 个 github 超牛逼项目!个人原创100W +访问量博客:点击前往,查看更多 作者:双子孤狼 bl ...

  2. linux打包日志时间,打包压缩_任务计划_日志

    tar命令 功能:文件的打包.解包,并且可以实现压缩功能. 打包压缩语法:tar  -czvf   文件名.tar.gz   要打包的文件列表 解包解压缩语法:tar  -xvf   文件名.tar. ...

  3. 的日志在哪里_写给大忙人看的,MyBatis日志如何做到兼容所有常用的日志框架?...

    前言 日志,在我们开发中是一个非常重要的话题,良好的日志打印可以帮助我们快速的定位问题,可能现在我们开发用到最多的日志框架就是slf4j了,但是日志还有其他很多优秀的框架,比如:Apache Comm ...

  4. Mybatis整体学习笔记-CRUD-配置解析-结果集映射-日志-注解开发-复杂环境-动态SQL-缓存

    MyBatis 要多对官方文档进行学习 https://mybatis.org/mybatis-3/zh/index.html 简介 MyBatis 持久层框架 Dao Access Objects ...

  5. SpringBoot整合:Druid、MyBatis、MyBatis-Plus、多数据源、knife4j、日志、Redis,Redis的Java操作工具类、封装发送电子邮件等等

    SpringBoot笔记 一.SpringBoot 介绍 1.1.SpringBoot简介 SpringBoot 是一个快速开发的框架, 封装了Maven常用依赖.能够快速的整合第三方框架:简化XML ...

  6. 一文读懂常用日志框架(Log4j、SLF4J、Logback)有啥区别

    1.概述 相信目前大多数情况下,不管是开源框架或是平时我们工作编码中都离不开一种框架,它就是日志框架.因此本节就简单了解一下我们常用日志框架的区别. 2.Commons Logging common- ...

  7. Java常用日志框架介绍

    Java常用日志框架介绍 文章目录 Java常用日志框架介绍 日志接口和日志框架的区别 各个框架出现的时间线 桥接包 Log4j2 常用搭配 单独的log4j 使用JCL(commons-loggin ...

  8. logback日志pattern_003、Spring Boot使用slf4j进行日志记录

    在开发中,我们经常使用 System.out.println() 来打印一些信息,但是这样不好,因为大量的使用 System.out 会增加资源的消耗.我们实际项目中使用的是 slf4j 的 logb ...

  9. java日志系统简介: 从tomcat大量打印debug日志说起

    博客搬家至 https://lcy362.github.io/posts/4433/ 目前,java下应用最广泛的日志系统主要就是两个系列: log4j和slf4j+logback . 其中,slf4 ...

最新文章

  1. R语言unique函数计算数据对象(vector、dataframe)的unique独特值:unique函数从vector向量、dataframe中删除重复项、删除dataframe重复行
  2. php jquery选择器,常用jQuery选择器总结_jquery
  3. 计算机一级电子表格TF函数,tf.slice()函数详解(极详细)
  4. .NET 6新特性试用 | DateOnly和TimeOnly类型
  5. 【Pytorch神经网络理论篇】 30 图片分类模型:Inception模型
  6. hdu 2516 取石子游戏
  7. ARMedia问题记录
  8. centos6 安装和配置PHP 7.0
  9. 在禁用视图状态的情况下仍然使用ViewState对象
  10. 简单计算器——两种方法
  11. Unity WIndows语音识别(一)关键字识别
  12. idea 项目启动找不到页面问题和run/debug只能启动一个的问题
  13. m3u8格式转换器android,m3u8转换格式mp4软件下载-m3u8转换格式 安卓版v2.7.0-PC6安卓网...
  14. 开关电源—Buck电路原理及其仿真
  15. arduino nano关于microUSB无法用的处理
  16. 推荐几款软件界面模型设计工具
  17. 聚类生成anchor框的尺寸和比例
  18. 八皇后问题(又名: 高斯八皇后)
  19. EasyPoi导出Excel实现标记颜色
  20. centos8搭建maven私服(含nexus-3.28.1-01.tar.gz下载链接)

热门文章

  1. Linux下运行java DES解密失败,报javax.crypto.BadPaddingException:Given final block not properly padded
  2. vector机器人 WHERE TO USE VECTOR 在哪里使用 VECTOR
  3. docker 容器互访三种方式
  4. google chrome 谷歌浏览器 快捷键说明
  5. redis 多数据库 database配置项解析
  6. 什么是CPython
  7. linux下的 ifconfig 和 ip 命令
  8. 如何同时启动多个Tomcat服务器
  9. Linux System Programming --Chapter Seven
  10. 手机1像素线粗_豪威推出4800万像素手机传感器:1/2大底