• Mybatis内部模块之---日志模块


    1.设计模式

    首先日志模块采用的是适配器模式:因为日志厂商,没有专门针对Mybatis的专门日志模块。所以Mybatis要引入外部的日志模块,于是才用了该设计模式

    2.日志模块所在位置

    org.apache.ibatis.logging

    例:

    //jdkLog的适配器,实现的是Log接口(Mybatie自己规定的日志需要有的能力)
    public class Jdk14LoggingImpl implements Log {
    
      //真正提供日志能力的jdk的日志类(这就是引入的外部实现类)
      private final Logger log;
    
      public Jdk14LoggingImpl(String clazz) {
        log = Logger.getLogger(clazz);
      }
    
      @Override
      public boolean isDebugEnabled() {
        return log.isLoggable(Level.FINE);
      }
    
      @Override
      public boolean isTraceEnabled() {
        return log.isLoggable(Level.FINER);
      }
    
      @Override
      public void error(String s, Throwable e) {
        log.log(Level.SEVERE, s, e);
      }
    
      @Override
      public void error(String s) {
        log.log(Level.SEVERE, s);
      }
    
      @Override
      public void debug(String s) {
        log.log(Level.FINE, s);
      }
    
      @Override
      public void trace(String s) {
        log.log(Level.FINER, s);
      }
    
      @Override
      public void warn(String s) {
        log.log(Level.WARNING, s);
      }
    
    }

    3.Mybatis又是怎样加载到Mybatis框架里面的

    下面我们看看 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;
    
      //自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog
      static {
        tryImplementation(LogFactory::useSlf4jLogging);
        tryImplementation(LogFactory::useCommonsLogging);
        tryImplementation(LogFactory::useLog4J2Logging);
        tryImplementation(LogFactory::useLog4JLogging);
        tryImplementation(LogFactory::useJdkLogging);
        tryImplementation(LogFactory::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
          }
        }
      }
      //通过指定的log类来初始化构造方法
      private static void setImplementation(Class<? extends Log> implClass) {
        try {
          Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
          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);
        }
      }
    
    }
    View Code

    4.日志能力又是怎么样集成到Mybatis运行当中的呢

    4.1. 采用的是动态代理实现的。

    4.2. 在logging中的jdbc里面是Mybatis增强的地方

    4.3. 关于类的组成结构,我们来看一下。

    BaseJdbcLogger:所有日志增强的抽象基类,所有日志增强的父类

    子类:

             ConnectionLogger 负责打印连接信息,和sql语句,并创建PreparedStatementLogger
             PreparedStatementLogger 负责打印参数信息,并创建ResultSetLogger
             ResultSetLogger 负责打印结果信息
            StatementLogger

            上面的几个类,父类都是 BaseJdbcLogger ,并且都实现了InvocationHandler

     我们就看一个简单的例子就行了:

    /**
     *    Copyright ${license.git.copyrightYears} the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.logging.jdbc;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    
    import org.apache.ibatis.logging.Log;
    import org.apache.ibatis.reflection.ExceptionUtil;
    
    /**
     * Connection proxy to add logging
     * 
     * @author Clinton Begin
     * @author Eduardo Macarron
     * 
     */
    public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
    
     //真正的连接对象
      private final Connection connection;
    
      private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
        super(statementLog, queryStack);
        this.connection = conn;
      }
    
      @Override
      //对连接的增强
      public Object invoke(Object proxy, Method method, Object[] params)
          throws Throwable {
        try {
            //如果是从Obeject继承的方法直接忽略
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, params);
          }
          //如果是调用prepareStatement、prepareCall、createStatement的方法,打印要执行的sql语句
          //并返回prepareStatement的代理对象,让prepareStatement也具备日志能力,打印参数
          if ("prepareStatement".equals(method.getName())) {
            if (isDebugEnabled()) {
              debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);//打印sql语句
            }        
            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);//打印sql语句
            }        
            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);
        }
      }
    
      /*
       * Creates a logging version of a connection
       *
       * @param conn - the original connection
       * @return - the connection with logging
       */
      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);
      }
    
      /*
       * return the wrapped connection
       *
       * @return the connection
       */
      public Connection getConnection() {
        return connection;
      }
    
    }
    View Code

    5.日志集成到流程中

    让我们想一下,现在日志的基本知识我们知道了,但是日志又是怎么样集成到流程中呢,让我们看看

    类:SimpleExecutor.java
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
      }
    
    类:BaseExecutor.java
    protected Connection getConnection(Log statementLog) throws SQLException {
        Connection connection = transaction.getConnection();
        if (statementLog.isDebugEnabled()) {
          return ConnectionLogger.newInstance(connection, statementLog, queryStack);
        } else {
          return connection;
        }
      }
    
    类:ConnectionLogger.java
    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);
      }
    View Code
  • 相关阅读:
    centos6固定ip地址
    jenkins slave 安装服务与卸载
    jenkins slave 挂载
    Caused by: org.apache.velocity.exception.MethodInvocationException: Invocation of method 'getUser' in class org.uncommons.reportng.ReportMetadata threw exception class java.net.UnknownHostException :
    linux 修改时间永久
    linux 安装git环境变量配置
    liunx 安装maven
    maven3 下载列表
    jenkins -Djava.awt.headless=true Linux下java.awt.HeadlessException的解决办法
    【BZOJ4462】【JSOI2013】—编程作业(KMP)
  • 原文地址:https://www.cnblogs.com/lys-lyy/p/11497591.html
Copyright © 2020-2023  润新知