CoderLi

V1

2022/03/24阅读:30主题:前端之巅同款

Mybatis 日志与设计模式

微信公众号_CoderLi 适配器设计模式

原有系统已经从各个供应商中获取到航班数据、格式为 XML、并且将该数据传输给一个智能分析系统中做统计分析。最近业务改动、对接了一个新的智能分析系统、但是对方的输入格式为 Json、这个时候我们该怎么办?

我们可以创建一个适配器、该适配器实现原来的智能分析接口、在适配器里面做 XML 到 JSON 的转换、然后再真正的调用新的智能分析系统。对于应用系统而言、甚至可以不改动代码。

img
img

Mybatis 的适配器模式

现存主流的日志框架有 Logback、Log4j、Log4j2 等、但是大部分都是先使用 SLF4J 作为门面、通过其底层适配到具体的日志框架中。

Mybatis 中定义了自己的内部 Log 接口、然后通过适配器模式适配应用真实使用的日志框架

package org.apache.ibatis.logging;
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);

}

Log 接口就相当于 Client Interface

Jdk14LoggingImpl 这个就相当于适配器

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);
  }
}

java.util.logging.Logger 则相当于被适配的服务。

微信公众号:CoderLi
微信公众号:CoderLi

Mybatis 这种场景不像上面所说的供应商数据分析那种场景那么明显、因为好像 Mybatis 在项目前期就可以选择一款日志框架一样、但是并不是、它更想使用应用本身的日志框架。

或者这么说、Mybatis 自身就有了一套日志接口、面对市面上多种多样的日志框架、Mybatis 自身的接口与这些框架不兼容、而 Mybatis 更加不可能改动这些已经存在的第三方日志框架、所以它只能一个个去适配这些可能被应用使用到的框架。换这种角度、就非常适合。

微信公众号:CoderLi
微信公众号:CoderLi

可以看到 Mybatis 为了各种日志框架而做的各种适配器

再看看 Mybatis 如何选择出合适的适配器的

// LogFactory
  static {
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }
  public static synchronized void useSlf4jLogging() {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }

微信公众号:CoderLi
微信公众号:CoderLi

尝试去创建每种适配器、如果成功则说明了应用有使用到该日志框架、看到优先的是日志门面 LF4J

优点

  • 单一职责原则、可以将接口转换和数据转换从原有的逻辑中抽离、每个日志适配器类中、都是各个日志框架具体的调用、当然、日志的接口都是比较简单、基本都是转发接口、当然也存在其他复杂的情况
  • 开闭原则、Mybatis 只需要使用自己本来自定义的 Logger 接口即可、不需要知道具体日志框架。当然在上面的例子中甚至使用了静态工厂的方法、使应用甚至都不知道具体的适配器类或者接口的具体实现。即使以后市面上又出现另一个流行的日志框架、对于 Mybatis 来说、只是需要增加一个适配器类。(修改静态工厂这个是另外的话题、跟适配器模式没啥关系)

缺点

  • 很明显、代码复杂度高了、单纯适配器都好几个了。在这里如果直接使用某一个确定的日志框架、那么就不会存在这些适配器类了、但是这样的话、应用如果使用不同的框架、那么应用就需要配置维护两个日志框架了(当然可以使用 SL4J 再桥接过去)、这样子显然是非常不友好的。

与其他设计模式的关系

  • 桥接模式、桥接模式通常更加适用于开发前期设计、使你能够将程序的各个部分独立开来以便开发。适配器模式通常在已有的程序中使用、让相互不兼容的类能很好的合作
  • 装饰模式能在不改变对象接口的前提下强化对象的功能、并且可以递归组合、而适配器模式可以对接口进行修改(如果是新增一个新的功能、这个功能也是需要适配各种服务)、适配器模式则不能递归组合
  • 适配器能为被封装对象提供不同的接口、代理模式能为对象提供相同的接口、装饰则能为对象提供加强接口。

分类:

后端

标签:

后端

作者介绍

CoderLi
V1