zoukankan      html  css  js  c++  java
  • mybatis原理与设计模式-日志模块- 适配器模式

     在讲设计模式之前,得先知道java程序设计中得六大原则,才能更好得理解我们得系统为什么需要设计模式

    1 单一职责原则

      一个类只负责一种职责,只有这种职责的改变会导致这个类的变更。绕口一点的正统说法:不要存在多于一个原因导致类变更

      假如:类T 负责有两种职责 P1,P2;当P1发生改变时,需要修改类T,这时候可能会对P2造成影响。

      所以不要为了图代码量少,二将不同职责放入到一个类里面。

    2 里氏替换原则

      只要父类出现的地方,都可以用子类替换,并且不会对程序造成影响,在实现上来说就是子类不要覆盖父类的非抽象方法,但可以重载。

      重载时需要注意,入参的要求要比父类宽松(保证可以进入),返回要比父类更加严格(保证出去不会有问题),这也正是实现里氏替换的基础。

    3 依赖倒置原则

      高层模块不应该依赖低层模块,二者都应该依赖其抽象,翻译一下就是面向接口编程;接口一般是行为的集合,也就是尽可能的对行为抽象。

      抽象不应该依赖细节,细节应该依赖抽象。

    4 接口隔离原则

      翻译一下就是接口的功能尽可能单一,接口本质上是两个类之间关系的纽带,关系中不需要有的,在接口中不应该体现。如:A 通过接口I依赖B,假如接口I中有A 不需要的方法,那么这个接口就是不合理的,B必须要实现这个不需要的方法,徒劳无功。

    5 迪米特法则(最少知道原则)

      也就是说一个对象要对其他对象保持尽可能少的了解,即低耦合性,低耦合可以最大限度的保证代码的可复用性。这个实际上是针对被依赖的类来说的,对于被依赖的类,尽可能的将复杂的逻辑封装起来,对外只提供public方法,外部不需要知道内部的逻辑。

    6 开闭原则

      尽量通过扩展来面对需求的更改或者系统的变化,尽量不要对原有内容修改。

    在这六大原则中,开闭原则应该是最基础得原则

    适配器模式

    在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。在实际开发中,对象适配器的使用频率更高

    在对象适配器模式结构图中包含如下几个角色:

    Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

    Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

    Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码

    MyBatis的日志功能源码包logging就是适配器模式的应用,主要是为了将市场的各种日志插件,转换为MyBatis的日志接口,统一使用,下面是日志包的包结构

     其中我们可以重点关注下LogFactory这个工厂类

    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() {
          public void run() {
            useSlf4jLogging();
          }
        });
        tryImplementation(new Runnable() {
          public void run() {
            useCommonsLogging();
          }
        });
        tryImplementation(new Runnable() {
          public void run() {
            useLog4J2Logging();
          }
        });
        tryImplementation(new Runnable() {
          public void run() {
            useLog4JLogging();
          }
        });
        tryImplementation(new Runnable() {
          public void run() {
            useJdkLogging();
          }
        });
        tryImplementation(new Runnable() {
          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(new Object[] { 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
          }
        }
      }
    
     
    
    }

    在LogFactory类加载的时候会执行静态代码块,其逻辑是按序加载并实例化对应的日志组件的适配器,然后使用Logfactory.logConstructor这个静态字段,记录当前使用的的第三方日志组件的适配器

    tryImplementation(new Runnable() {
          public void run() {
            useLog4JLogging();
          }
        });

    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(new Class[] { String.class }); Log log = candidate.newInstance(new Object[] { LogFactory.class.getName() }); log.debug("Logging initialized using '" + implClass + "' adapter."); logConstructor = candidate; } catch (Throwable t) { throw new LogException("Error setting Log implementation. Cause: " + t, t); } }
     
    这个方法首先会检查logConstructor这个是不是空,如果为空则调用runnable.run方法,(注意不是start方法)


    接下来我们一起看一个其中的适配器类
    
    
    import org.apache.ibatis.logging.Log;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.slf4j.Marker;
    import org.slf4j.spi.LocationAwareLogger;

    public
    class Slf4jImpl implements Log { private Log log; public Slf4jImpl(String clazz) { Logger logger = LoggerFactory.getLogger(clazz); if (logger instanceof LocationAwareLogger) { try { // check for slf4j >= 1.6 method signature logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class); log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger); return; } catch (SecurityException e) { // fail-back to Slf4jLoggerImpl } catch (NoSuchMethodException e) { // fail-back to Slf4jLoggerImpl } } // Logger is not LocationAwareLogger or slf4j version < 1.6 log = new Slf4jLoggerImpl(logger); } public boolean isDebugEnabled() { return log.isDebugEnabled(); } public boolean isTraceEnabled() { return log.isTraceEnabled(); } public void error(String s, Throwable e) { log.error(s, e); } public void error(String s) { log.error(s); } public void debug(String s) { log.debug(s); } public void trace(String s) { log.trace(s); } public void warn(String s) { log.warn(s); } }
    Slf4jImpl实现了Log接口
    并在类中使用组合的方式引入Log的引入

    结合上面的适配模式可以分析,Slf4jImpl就是Apdater类,Log接口就是Target
    LoggerFactory.getLogger(clazz)这个操作就是获取  org.slf4j.Logger 他就是Adaptee类
    这里只分析其中一个实现类,感兴趣的可以去看其它的实现类,基本一致所以这里就不做讲解了

      没有英汉互译结果
      请尝试网页搜索
  • 相关阅读:
    155. 最小栈
    160. 相交链表
    PAT 1057 Stack
    PAT 1026 Table Tennis
    PAT 1017 Queueing at Bank
    PAT 1014 Waiting in Line
    PAT 1029 Median
    PAT 1016 Phone Bills
    PAT 1010 Radix
    PAT 1122 Hamiltonian Cycle
  • 原文地址:https://www.cnblogs.com/clovejava/p/9610707.html
Copyright © 2011-2022 走看看