承接上一篇关于spring 5.x的日志体系,本篇看看Mybatis的日志体系及实现,Mybatis版本基于3.x。

  关于mybatis的官方文档比较友好,分门别类,各有论述,如mybatis官方文档详见https://mybatis.org/mybatis-3/#,mybatis与spring的官方文档详见http://mybatis.org/spring/index.html,与springboot相关的参见http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/#,官方文档写的很完备,也适合初学者。

Mybatis环境搭建及日志展示

  研究mybatis日志,有必要写一个demo,该有的数据库(Mysql)相关的jar包依赖都应该有,pom文件如下。

 <dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency> <dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
version>8.0.13</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>

  配置mybatis环境:java config风格获取一个SqlSessionFactoryBean和动态动态注入一个数据源dataSource,连接本地的数据库company,表为user。

 @Configuration
@ComponentScan("com.mystyle")
@MapperScan("com.mystyle.dao")
public class MybatisConfig {
@Bean
@Autowired
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
// org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// configuration.setLogImpl(Log4jImpl.class);
// sqlSessionFactoryBean.setConfiguration(configuration);
return sqlSessionFactoryBean;
} @Bean
public DataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setUsername("root");
driverManagerDataSource.setPassword("12345678");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/company?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return driverManagerDataSource; } }

  注:注释的代码请先忽略,后续会用到。

 写一个UserDao,查询出user表中的user信息

public interface UserDao {
@Select("select * from user")
List<Map<String,String>> queryUsers();
}

  运行完成之后,可以看到console成功打印出了user的信息,但是除此之外,并未看到我们想要的打印出sql语句等信息。

Mybatis日志代码跟踪

  在MybatisConfig的sqlSessionFactory方法中,new出了一个SqlSessionFactoryBean,在SqlSessionFactoryBean类中可以看到一个关于Logger的私有对象的声明并初始化。

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);

  在LogFactory类中,

 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 {
     //1.启动线程
tryImplementation(new Runnable() {
@Override
public void run() {
        //2。加载日志类,返回构造器
useSlf4jLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useCommonsLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useLog4J2Logging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useLog4JLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useJdkLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
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);
      //3. 试图去实例化一个实现类的日志对象
Log log = candidate.newInstance(LogFactory.class.getName());
if (log.isDebugEnabled()) {
log.debug("Logging initialized using '" + implClass + "' adapter.");
}
logConstructor = candidate;
} catch (Throwable t) {
throw new LogException("Error setting Log implementation. Cause: " + t, t);
}
} }

   LogFactory在加载时,有一个静态代码块,会依次去加载各种日志产品的实现,请其中的顺序是 SLF4J > Apache Commons Logging > Log4j 2 > Log4j > JDK logging。

  以SLF4J为例说明(其余类似):

  1. 执行tryImplementation方法:如果构造器logConstructor(LogFactory中的私有变量)不为空,启动一个线程去执行其中的run 方法。
  2. 具体是去执行useSlf4jLogging方法中的setImplementation方法,该方法会传入SLF4J的日志实现类的class对象:class org.apache.ibatis.logging.slf4j.Slf4jImpl。
  3. 试图去实例化一个实现类的日志对象,由于此时并未加入slf4j相关jar包依赖,所以会抛一个异常出去。

  在默认情况下,即是用户未指定得情况下,class org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl也就是Apache Commons Logging会默认实例化成功,spring默认使用此日志实现,由于存在该实现依赖,所以会被实例化成功,并返回其构造器。SqlSessionFactoryBean中调用的getLog方法实际是返回了一个基于JCL实现的日志对象。

用户指定Mybatis日志实现类

  假设用户需要使得Mybatis使用的日志是基于log4j的,同样,首先需要在pom中先添加基于log4j的相关依赖,log4j.properties配置文件添加,在sqlSessionFactoryBean实例化时通过configuration去手动指定logimpl为log4J。

 public void setLogImpl(Class<? extends Log> logImpl) {
if (logImpl != null) {
this.logImpl = logImpl;
LogFactory.useCustomLogging(this.logImpl);
}
}

  实际内部是去调用了LogFactory的useCustomLogging方法。

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

  其余和上述流程一样,把factory中的日志实现类更新为用户所指定的实现类。

日志打印效果

 DEBUG - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG - Creating a new SqlSession
DEBUG - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4716be8b] was not registered for synchronization because synchronization is not active
DEBUG - JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3faf2e7d] will not be managed by Spring
DEBUG - ==> Preparing: select * from user
DEBUG - ==> Parameters:
DEBUG - <== Total: 7
DEBUG - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4716be8b]
[{eventId=1, count_num=1, name=tom, id=1}, {eventId=2, count_num=2, name=jack, id=2}, {eventId=7, count_num=2, name=david, id=3}, {eventId=3, count_num=2, name=david, id=4}, {eventId=4, count_num=0, name=david, id=5}, {eventId=5, count_num=1, name=david, id=6}, {eventId=6, count_num=0, name=lily, id=7}]
Disconnected from the target VM, address: '127.0.0.1:53155', transport: 'socket'

  可以看到日志打印出的格式以及内容都是我们所想要的。

  

Mybatis日志体系的更多相关文章

  1. Mybatis日志源码探究

    一.项目搭建 1.pom.xml <dependencies> <dependency> <groupId>log4j</groupId> <ar ...

  2. Oracle RAC的日志体系

    Oracle Clusterware 不像数据库那样,有丰富的视图.工具可以用来辅助诊断,它的日志和trace文件是唯一的选择.但不像Oracle只有alert日志和几种trace文件,Oracle ...

  3. Oracle RAC环境的日志体系

    转摘:http://blog.itpub.net/22664653/viewspace-722463/ 在Oracle RAC环境中比单个系统的日志体系要复杂:见下图: 简单介绍一下有关Oracle集 ...

  4. 笔记:MyBatis 日志显示-log4j2

    在ClassPath路径创建log4j2.xml配置文件,增加如下日志配置: <?xml version="1.0" encoding="UTF-8"?& ...

  5. 【原创】架构师必备,带你弄清混乱的JAVA日志体系!

    引言 还在为弄不清commons-logging-xx.jar.log4j-xx.jar.sl4j-api-xx.jar等日志框架之间复杂的关系而感到烦恼吗? 还在为如何统一系统的日志输出而感到不知所 ...

  6. java 日志体系目录

    java 日志体系目录 1.1 java 日志体系(一)log4j1.log4j2.logback.jul.jcl.slf4j 1.2 java 日志体系(二)jcl 和 slf4j 2.1 java ...

  7. java 日志体系(四)log4j 源码分析

    java 日志体系(四)log4j 源码分析 logback.log4j2.jul 都是在 log4j 的基础上扩展的,其实现的逻辑都差不多,下面以 log4j 为例剖析一下日志框架的基本组件. 一. ...

  8. java 日志体系(三)log4j从入门到详解

    java 日志体系(三)log4j从入门到详解 一.Log4j 简介 在应用程序中添加日志记录总的来说基于三个目的: 监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作: 跟踪代 ...

  9. Java 日志体系(二)jcl 和 slf4j

    Java 日志体系(二)jcl 和 slf4j <java 日志体系(一)统一日志>:https://www.cnblogs.com/binarylei/p/9828166.html &l ...

随机推荐

  1. OptimalSolution(5)--数组和矩阵问题(1)简单

    一.转圈打印矩阵 题目:给定一个整型矩阵matrix,按照转圈的方式打印它. 要求:额外空间复杂度为O(1) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 打印结果为: ...

  2. 提高首屏页面加载速度,解决vue-cli打包后单个文件过大的问题

    本教程是针对vue-cli3以上的版本,其实原理都大同小异,这个demo为vue-cli直接创建的项目,并在main.js中引入了echart.element-ui.lodash 首先看demo打包后 ...

  3. Java面试官最爱问的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  4. javascript创建一个基于数组的栈结构

    栈是一种遵从后进先出(LIFO)原则的有序集合.新添加或待删除的元素都保存在栈的同 一端,称作栈顶,另一端就叫栈底.在栈里,新元素都靠近栈顶,旧元素都接近栈底. 栈拥有以下方法: push(eleme ...

  5. csps51(a)

    T1的暴力死掉了...... T3大众分...... T2打了几个小表,发现了一些规律,成功yy出了$O(nlogalogn)$的暴力,拿到了73pts,弥补了爆炸的T1. T1. 考场上看了看,遥远 ...

  6. Python 基础之re 模块

    Python 基础之大话 re 在使用re模块中主要会用到一下几个方法: re.match() #从头匹配一个字符串 re.search() #浏览全部字符串,匹配第一个符合规则的字符串 re.fin ...

  7. java多线程回顾2:生命周期与控制

    1.  线程生命周期概述 线程的生命周期如下图: 2.  新建与就绪 当程序使用new关键字创建一个线程之后,线程就处于新建状态了.此时线程只是被分配了内存资源,初始化了成员变量. 当线程对象被调用了 ...

  8. jdk8 函数式编程概念

    yls 2019/11/7 函数式接口 如果一个接口只有一个抽象方法,那么该接口就是函数式接口 如果我们在某接口上声明了FunctionalInterface注解,那么编译器就会按照函数式接口的定义来 ...

  9. ReactJS的4行代码

    Angular 2一个显著的变动是,把Angular 1的Promise pattern改成了Observer pattern,并且使用了ReactJS.这里有一篇值得一读的文章 要搞懂ReactJS ...

  10. c/c++求最大公约数和最小公倍数

    最大公约数GCD(Greatest Common Divisor) 最常见的求两个数的最大公约数的算法是辗转相除法,也叫欧几里得算法 该算法的c++语言实现如下: #include<iostre ...