zoukankan      html  css  js  c++  java
  • mybatis源码解析-日志适配器

      1.为什么需要使用适配器?
          集成第三方日志组件,屏蔽日志组件底层实现,统一提供写日志的接口。

      2.什么是适配器模式
       定义:将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作。

      

      client-->Target(统一接口) Adapter继承Target,并封装Adaptee对象 Adaptee类做具体的工作
      Target目标角色:
      该角色定义把其他类转换为何种接口
      Adaptee源角色:
      你想把谁转换成目标角色,这个“谁”就是源角色,他是已经存在的角色。
      Adapter 适配器角色:
      适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责是把源角色转换为目标角色,实现方式是通过继承或者组合的方式.

      3.mybatis中日志适配

             

       从commons包到stdout都可视为适配器角色,Log接口为目标角色,LogFactory为创建具体日志组件适配器的工厂。

      适配关系如下:

           

      

      从源码角度看:
      目标角色:Log

    public interface Log {
    
      boolean isDebugEnabled();
    
      boolean isTraceEnabled();
    
      void error(String s, Throwable e);
    
      void error(String s);
    
      void debug(String s);
    
      void trace(String s);
    
      void warn(String s);
    }

      

      统一定义了日志级别。
      适配器角色,以Jdk14LoggingImpl 为例,

      

    package org.apache.ibatis.logging.jdk14;
    
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    import org.apache.ibatis.logging.Log;
    
    /**
     * @author Clinton Begin
     */
    public class Jdk14LoggingImpl implements Log {
    
      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);
      }
    
    }

      

      Jdk14LoggingImpl 通过继承关系实现Log,组合java.util.logging.Logger对象log; ,适配器实现继承的方法的时候通过关联对象log实现。
      4.mybatis怎么初始化日志适配器

    package org.apache.ibatis.logging;
    
    import java.lang.reflect.Constructor;
    
    /**
     * @author Clinton Begin
     * @author Eduardo Macarron
     */
    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 {
        tryImplementation(new Runnable() {
          @Override
          public void run() {
            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);
          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);
        }
      }
    
    }

      上面适配器工厂我们通过mybatis的官方文档中的日志模块的使用来进行辅助理解:
      官网说明如下:
      Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
      SLF4J
      Apache Commons Logging
      Log4j 2
      Log4j
      JDK logging
      MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。

      这段话和源码中的static静态代码块相对应。

    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();
          }
        });
        tryImplementation(new Runnable() {
          @Override
          public void run() {
            useLog4JLogging();
          }
        });
        tryImplementation(new Runnable() {
          @Override
          public void run() {
            useJdkLogging();
          }
        });
        tryImplementation(new Runnable() {
          @Override
          public void run() {
            useNoLogging();
          }
        });
      }

      

      官网中的说明顺序一样,
      以其中JdkLogging为例说明初始化日志适配过程:

    private static void tryImplementation(Runnable runnable) {
        if (logConstructor == null) {
          try {
            runnable.run();
          } catch (Throwable t) {
            // ignore
          }
        }
      }

      首先判断当前logConstructor日志适配器构造方法是否已经初始化,如果初始化则返回,如果没有初始化则执行run()方法,对应当前示例就是useJdkLogging()方法,

      

    public static synchronized void useJdkLogging() {
     交给了setImplementation
        setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
      }
      private static void setImplementation(Class<? extends Log> implClass) {
        try {
            //通过传入类型获取适配器构造方法 如果没有找到对应类型则返回null
          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);
        }
      }

      

      按上面次序进行日志适配器的初始化。

      最后一个useNoLogging代表禁用日志功能,因为适配器NoLoggingImpl中什么都没有做。

    package org.apache.ibatis.logging.nologging;
    
    import org.apache.ibatis.logging.Log;
    
    /**
     * @author Clinton Begin
     */
    public class NoLoggingImpl implements Log {
    
      public NoLoggingImpl(String clazz) {
        // Do Nothing
      }
    
      @Override
      public boolean isDebugEnabled() {
        return false;
      }
    
      @Override
      public boolean isTraceEnabled() {
        return false;
      }
    
      @Override
      public void error(String s, Throwable e) {
        // Do Nothing
      }
    
      @Override
      public void error(String s) {
        // Do Nothing
      }
    
      @Override
      public void debug(String s) {
        // Do Nothing
      }
    
      @Override
      public void trace(String s) {
        // Do Nothing
      }
    
      @Override
      public void warn(String s) {
        // Do Nothing
      }
    }

        官网说明第二段
       你也可以调用以下任一方法来选择日志实现:

    org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
    org.apache.ibatis.logging.LogFactory.useLog4JLogging();
    org.apache.ibatis.logging.LogFactory.useJdkLogging();
    org.apache.ibatis.logging.LogFactory.useCommonsLogging();
    org.apache.ibatis.logging.LogFactory.useStdOutLogging()

    你应该在调用其它 MyBatis 方法之前调用以上的某个方法。另外,仅当运行时类路径中存在该日志实现时,日志实现的切换才会生效。
    如果你的环境中并不存在 Log4J,你却试图调用了相应的方法,MyBatis 就会忽略这一切换请求,并将以默认的查找顺序决定使用的日志实现。


    因为上面的方法为LogFactory中的静态方法,所以可以直接调用来初始化适配器工厂中的适配器构造方法(logConstructor),从而实现第三方日志组件的切换,并且该方法执行于static静态代码块之后。

    本文参考:mybatis技术内幕,Mybatis官网

  • 相关阅读:
    HDU 3389 Game (阶梯博弈)
    国内操作系统OS分析(上)
    激光雷达应用技术分析
    构建深度学习框架运行平台
    GitHub上YOLOv5开源代码的训练数据定义
    GitHub上开源的YOLOv5
    深度学习调用TensorFlow、PyTorch等框架
    CUDA C 纹理提取Texture Fetching
    CPU,GPU,GPGPU
    毫米波RADAR与LIDAR探秘
  • 原文地址:https://www.cnblogs.com/hopeofthevillage/p/13063715.html
Copyright © 2011-2022 走看看