Mybatis source of parsing module parses: log module

Introduction

mybatis implementation class did not provide the log, log component needs access to third

LogFactory static block of code to third class can be seen as load priority log slf4j> apache commons log> log4j2> log4j> jul

  static {
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useSlf4jLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useCommonsLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useLog4J2Logging();
      }
    });
	
	// 省略了其他模块的加载逻辑
  }

As can be seen from LogFactory class name used in the factory pattern, factory pattern do not understand you can see the following articles

Factory mode (static factory pattern, factory method pattern, abstract factory pattern) Comments

But the third party have their own log-log, mybatis adapter provides a unified model trace, debug, warn, error of four levels,

org.apache.ibatis.logging.Log interface has multiple implementation classes, the implementation class that is provided by the adapter Mybatis
, e.g. Log4jImpl, Log4j2Impl the like, an adapter class provides an implementation, following the UML class diagram

Here Insert Picture Description

Logger figure above objects are org.apache.log4j.Logger, i.e., by the operation of the conversion will Log4jImpl Log interface adapted to the operation target Logger

Adapter pattern design follows several roles

  1. Target Interface (Target): The caller interfaces can be used directly, that is, the interface Log
  2. Adapter required classes (Adaptee): Adaptee class has a real logical, but the caller can not be used directly, that Logger object
  3. Adapter (Adapter): Target implements the interface, packed Adaptee objects

Of course, we generally do not configure the Logger object after Mybatis fit in the project, because the level is too low, INFO level are not

import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
public static final Log log = LogFactory.getLog(Test.class);

Configured to direct the slf4j

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public static final Logger logger = LoggerFactory.getLogger(Test.class);

To adaptation of all, because the JDBC module will print the log, but it is not binding and specific logging framework, using the Log object after Mybatis adaptation

JDBC debugging, print different types of logs

In mybatis source it has a logging module jdbc package, when the log level to DEBUG level its output a lot of useful information in a dynamic manner agents, such as the output of SQL statements, the user parameter bindings that are passed, the number of rows and the like affect the SQL statement information

You might be thinking, why should print debug level log through dynamic proxy it? With log.debug () not on the list, mainly to avoid the log normal logic and coupled to a logic

BaseJdbcLogger is an abstract class that is the parent of other Logger class under jdbc package, inheritance following figure
Here Insert Picture Description
ConnectionLogger: responsible for printing the connection information and SQL statements, and create PreparedStatementLogger
PreparedStatementLogger: responsible for printing parameter information, and create ResultSetLogger
StatementLogger: responsible for printing parameters information, and create ResultSetLogger
ResultSetLogger: responsible for printing the data results information

4 class implements the idea is the same , just to analyze ConnectionLogger

When the log level to debug, returns a Connection object after being proxy, otherwise normal Connection object

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

ConnectionLogger InvocationHandler object implements the interface, the agent returned after the Connection object

  public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
    InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
    ClassLoader cl = Connection.class.getClassLoader();
    return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
  }

Specific interception logic is as follows

  @Override
  public Object invoke(Object proxy, Method method, Object[] params)
      throws Throwable {
    try {
      // 如果是从Object继承的方法直接忽略
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }
      // 如果是调用prepareStatement,prepareCall,createStatement的方法,打印要执行的SQL语句
      // 并返回PreparedStatement的代理对象,让PreparedStatement也具备日志能力,打印参数
      if ("prepareStatement".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }        
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("prepareCall".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }        
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("createStatement".equals(method.getName())) {
        Statement stmt = (Statement) method.invoke(connection, params);
        stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else {
        return method.invoke(connection, params);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

We can see the three methods were enhanced prepareStatement, prepareCall, createStatement

If the method is to call prepareStatement, prepareCall of, SQL statements to be executed print
three methods will return a proxy object PreparedStatement, so PreparedStatement also have the ability to log, print parameters

See the final printed DEBUG log

DEBUG 2020-02-24 18:09:13,647 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select id, `name`, phone from author 
DEBUG 2020-02-24 18:09:13,766 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 
DEBUG 2020-02-24 18:09:13,813 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1

Is not looked very familiar with, it is to use dynamic proxies to achieve

Welcome attention

Here Insert Picture Description

Published 385 original articles · won praise 1471 · Views 900,000 +

Guess you like

Origin blog.csdn.net/zzti_erlie/article/details/104398887