盘点MyBatis优良设计之接口及其实现类间的故事

一、前言

    接口及其实现类,这种关系大多数开发者多多少少还是知道的。但是面对一个接口多个实现类的设计及其应用,相信绝大多数的开发者还是手足无措的。接口以及抽象类的概念在这里不哆嗦,基础知识务必扎实,才不会在源码的世界里面总是走迷宫。而MyBatis源码里面就有不少关于接口及其实现类的良好设计,在这里笔者罗列一些设计供大家学习参考参考。

    不知道大家是否记得上转型的概念,不记得的就得下点功夫了。

二、接口及其实现类的秘密

1、分散new实现类形式

1)接口及其实现类

盘点MyBatis优良设计之接口及其实现类间的故事_第1张图片

2)PropertyParser的parse()方法

  public static String parse(String string, Properties variables) {
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    return parser.parse(string);
  }

3)TextSqlNode中的方法

  // 判断是否是动态sql
  public boolean isDynamic() {
    DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
    GenericTokenParser parser = createParser(checker);
    parser.parse(text);
    return checker.isDynamic();
  }

  @Override
  public boolean apply(DynamicContext context) {
    GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
    context.appendSql(parser.parse(text));
    return true;
  }
  
  private GenericTokenParser createParser(TokenHandler handler) {
    return new GenericTokenParser("${", "}", handler);
  }

2、集中new实现类形式,封装到一个方法里面

2.1、放到集合map形式

1)接口及其实现类

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2h5biD5aWH6K-6Lea1t-aZqA==,size_20,color_FFFFFF,t_70,g_se,x_16

2)XMLScriptBuilder中的parseDynamicTags()与nodeHandlers()方法

  List parseDynamicTags(XNode node) {
    List contents = new ArrayList();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        String nodeName = child.getNode().getNodeName();
        NodeHandler handler = nodeHandlers(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return contents;
  }

  NodeHandler nodeHandlers(String nodeName) {
    Map map = new HashMap();
    map.put("trim", new TrimHandler());
    map.put("where", new WhereHandler());
    map.put("set", new SetHandler());
    map.put("foreach", new ForEachHandler());
    map.put("if", new IfHandler());
    map.put("choose", new ChooseHandler());
    map.put("when", new IfHandler());
    map.put("otherwise", new OtherwiseHandler());
    map.put("bind", new BindHandler());
    return map.get(nodeName);
  }

2.2、非map形式

如下面3.2、3.4.2

三、匹配模式

3.1、instanceof匹配模式

判断左边的对象是否是右边的类型或者子类型

3.2、if成员变量条件匹配模式

1)接口及其实现类

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2h5biD5aWH6K-6Lea1t-aZqA==,size_20,color_FFFFFF,t_70,g_se,x_16

2)CacheBuilder的setStandardDecorators()方法

  // 最后附加上标准的装饰者
  private Cache setStandardDecorators(Cache cache) {
    try {
      MetaObject metaCache = SystemMetaObject.forObject(cache);
      if (size != null && metaCache.hasSetter("size")) {
        metaCache.setValue("size", size);
      }
      if (clearInterval != null) {
        // 刷新缓存间隔,怎么刷新呢,用ScheduledCache来刷,还是装饰者模式,漂亮!
        cache = new ScheduledCache(cache);
        ((ScheduledCache) cache).setClearInterval(clearInterval);
      }
      if (readWrite) {
          // 如果readOnly=false,可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
        cache = new SerializedCache(cache);
      }
      // 日志缓存
      cache = new LoggingCache(cache);
      // 同步缓存, 3.2.6以后这个类已经没用了,考虑到Hazelcast, EhCache已经有锁机制了,所以这个锁就画蛇添足了。
      cache = new SynchronizedCache(cache);
      if (blocking) {
        cache = new BlockingCache(cache);
      }
      return cache;
    } catch (Exception e) {
      throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
    }
  }

3.3、map的key匹配形式

如:上面的2.1

3.4、封装type匹配模式

3.4.1、switch模式

1)接口及其实现类

盘点MyBatis优良设计之接口及其实现类间的故事_第2张图片

2)RoutingStatementHandler构造方法

  public RoutingStatementHandler(Executor executor,
                                 MappedStatement ms,
                                 Object parameter,
                                 RowBounds rowBounds,
                                 ResultHandler resultHandler,
                                 BoundSql boundSql) {

    //根据语句类型,委派到不同的语句处理器(STATEMENT|PREPARED|CALLABLE)
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

3.4.2、if模式

1)接口及其实现类

盘点MyBatis优良设计之接口及其实现类间的故事_第3张图片

2)Configuration的newExecutor()方法

  //产生执行器
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    //这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //此处调用插件,通过插件可以改变Executor行为
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

总结

如果对你有帮助帮忙点点赞!!!

需要完善完整版本的可私信我哦,你懂得!​​​​​​​

你可能感兴趣的:(MyBatis,架构)