详解MyBatis日志如何做到兼容所有常用的日志框架

前言

日志,在我们开发中是一个非常重要的话题,良好的日志打印可以帮助我们快速的定位问题,可能现在我们开发用到最多的日志框架就是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是如何通过动态代理将不同日志类型分为不同对象来实现日志打印功能的。

到此这篇关于详解MyBatis日志如何做到兼容所有常用的日志框架的文章就介绍到这了,更多相关MyBatis兼容所有日志框架内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MyBatis启动时控制台无限输出日志的原因及解决办法

    你是否遇到过下面的情况,控制台无限的输出下面的日志: Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. Logging initialized using 'class org.apache.ibatis.lo

  • MyBatis是如何实现日志模块的详解

    场景复现 你知道MyBatis是怎么实现日志的?额,这个简单,我知道啊!不就是在mybatis-config.xml文件中配置一下吗? <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <set

  • MyBatis源码分析之日志logging详解

    前言 本文介绍个人对 logging 包下源码的理解.分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 logging 配置加载 我们先从日志的配置加载开始阅读, MyBatis 的各项配置的加载过程都可以从 XMLConfigBuilder 类中找到,我们定位到该类下的日志加载方法 loadCustomLogImpl: private void loadCustomLogImpl(Properties props) { // 从 MyBatis 的 TypeAliasRegist

  • MyBatis源码分析之日志记录详解

    一 .概述 MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,但MyBatis统一提供了trace.debug.warn.error四个级别: 自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog; 日志的使用要优雅的嵌入到主体功能中: 二.设计模式 将各种日志组件如(slf4J ,commonsLoging ,Log4J2 , Log4J

  • Spring Boot集成Mybatis中如何显示日志的实现

    在Spring Boot集成Mybatis的项目中,如果出现SQL语句执行问题,我们需要进行排查.此时就需要打印对应的SQL语句,那么该如何操作才能正常打印出对应的SQL语句呢? 其实操作非常简单,在application.properties或application.yml文件中配置对应mapper所在包的日志级别即可. 示例如下: logging.level.com.secbro.mapper=debug 其中"logging.level."为前缀,"com.secbro

  • MyBatis Plus配置日志CRUD的使用详解

    配置日志 我们所有的sql在mybatisplus是不可见的,所以在开发中需要配置日志,开发完成后,就可以取消日志了,因为日志也是损耗资源的 #配置日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #仅控制台输出的日志 配置完日志后,后面的学习就需要注意自动生成的SQL,相信你们会喜欢上MybatisPlus CRUD使用 Insert插入 //测试插入 @Test publ

  • 详解MyBatis日志如何做到兼容所有常用的日志框架

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

  • 详解MyBatis XML配置解析

    MyBatis核心配置文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environm

  • 详解Mybatis是如何解析配置文件的

    缘起 经过前面三章的入门,我们大概了解了Mybatis的主线逻辑是什么样子的,在本章中,我们将正式进入Mybatis的源码海洋. Mybatis是如何解析xml的 构建Configuration 我们调用new SqlSessionFactoryBuilder().build()方法的最终目的就是构建 Configuration对象,那么Configuration何许人也?Configuration对象是一个配置管家, Configuration对象之中维护着所有的配置信息. Configura

  • 详解Mybatis拦截器安全加解密MySQL数据实战

    需求背景 公司为了通过一些金融安全指标(政策问题)和防止数据泄漏,需要对用户敏感数据进行加密,所以在公司项目中所有存储了用户信息的数据库都需要进行数据加密改造.包括Mysql.redis.mongodb.es.HBase等. 因为在项目中是使用springboot+mybatis方式连接数据库进行增删改查,并且项目是中途改造数据.所以为了不影响正常业务,打算这次改动尽量不侵入到业务代码,加上mybatis开放的各种拦截器接口,所以就以此进行改造数据. 本篇文章讲述如何在现有项目中尽量不侵入业务方

  • 详解MyBatis的getMapper()接口、resultMap标签、Alias别名、 尽量提取sql列、动态操作

    一.getMapper()接口 解析:getMapper()接口 IDept.class定义一个接口, 挂载一个没有实现的方法,特殊之处,借楼任何方法,必须和小配置中id属性是一致的 通过代理:生成接口的实现类名称,在MyBatis底层维护名称$$Dept_abc,selectDeptByNo() 相当于是一个强类型 Eg 第一步:在cn.happy.dao中定义一个接口 package cn.happy.dao; import java.util.List; import cn.happy.e

  • 详解MyBatis Generator自动创建代码(dao,mapping,poji)

    连接的数据库为SQL server2008,所以需要的文件为sqljdbc4.jar 使用的lib库有: 在lib库目录下新建一个src文件夹用来存放生成的文件,然后新建generatorConfig.xml 里面代码为: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis G

  • 详解MyBatis直接执行SQL查询及数据批量插入

    一.直接执行SQL查询: 1.mappers文件节选 <resultMap id="AcModelResultMap" type="com.izumi.InstanceModel"> <result column="instanceid" property="instanceID" jdbcType="VARCHAR" /> <result column="insta

  • 详解MyBatis逆向工程

    1.什么是mybatis逆向工程 在使用mybatis时需要程序员自己编写sql语句,针对单表的sql语句量是很大的,mybatis官方提供了一种根据数据库表生成mybatis执行代码的工具,这个工具就是一个逆向工程. 逆向工程:针对数据库单表-->生成代码(mapper.xml.mapper.java.pojo..) mybatis-generator-core-1.3.2.jar-逆向工程运行所需要的jar核心 包 2.配置逆向工程的配置文件 配置文件generatorConfig.xml

  • 详解Mybatis框架SQL防注入指南

    前言 SQL注入漏洞作为WEB安全的最常见的漏洞之一,在java中随着预编译与各种ORM框架的使用,注入问题也越来越少.新手代码审计者往往对Java Web应用的多个框架组合而心生畏惧,不知如何下手,希望通过Mybatis框架使用不当导致的SQL注入问题为例,能够抛砖引玉给新手一些思路. 一.Mybatis的SQL注入 Mybatis的SQL语句可以基于注解的方式写在类方法上面,更多的是以xml的方式写到xml文件.Mybatis中SQL语句需要我们自己手动编写或者用generator自动生成.

  • 详解Mybatis中的 ${} 和 #{}区别与用法

    Mybatis 的Mapper.xml语句中parameterType向SQL语句传参有两种方式:#{}和${} 我们经常使用的是#{},一般解说是因为这种方式可以防止SQL注入,简单的说#{}这种方式SQL语句是经过预编译的,它是把#{}中间的参数转义成字符串,举个例子: select * from student where student_name = #{name} 预编译后,会动态解析成一个参数标记符?: select * from student where student_name

随机推荐