zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    The formatter threw an exception while trying to deserialize the message in WCF
    通过Web Deploy方式部署WCF
    The Managed Metadata Service or Connection is currently not available
    How to create Managed Metadata Column
    冒泡算法
    asp.net core 实战项目(一)——ef core的使用
    Vue学习笔记入门篇——安装及常用指令介绍
    Vue学习笔记入门篇——数据及DOM
    Vue学习笔记目录
    Chart.js在Laravel项目中的应用
  • 原文地址:https://www.cnblogs.com/lys-lyy/p/11497591.html
Copyright © 2011-2022 走看看