zoukankan      html  css  js  c++  java
  • mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)

    @

    相关文章推荐
    mybatis 缓存的使用, 看这篇就够了
    mybatis源码-解析配置文件(四)之配置文件Mapper解析

    1. 简介

    本文章主要讲解的是, xxxMapper.xml 文件中, cache 节点的源码。

    2. 解析

    XMLMapperBuilder.cacheElement() 方法主要负责解析 <cache>

      private void cacheElement(XNode context) throws Exception {
        if (context != null) {
          // 获取 type 节点的属性, 默认是 PERPETUAL
          String type = context.getStringAttribute("type", "PERPETUAL");
          // 通过 type 值, 查找对应 Cache 接口的实现
          Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
          // eviction 属性, eviction 对应的是回收策略, 默认为 LRU。
          String eviction = context.getStringAttribute("eviction", "LRU");
          // 解析 eviction 属性指定的 Cache 装饰器类型
          Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
          // flushInterval 对应刷新间隔, 单位毫秒, 默认值不设置, 即没有刷新间隔, 缓存仅仅在刷新语句时刷新。
          Long flushInterval = context.getLongAttribute("flushInterval");
          // size 对应为引用的数量,即最多的缓存对象数据。
          Integer size = context.getIntAttribute("size");
          // readOnly 为只读属性, 默认为 false, 即可读写
          boolean readWrite = !context.getBooleanAttribute("readOnly", false);
          // blocking 为阻塞, 默认值为 false。 当指定为 true 时将采用 BlockingCache 进行封装
          boolean blocking = context.getBooleanAttribute("blocking", false);
          // 获取 <cache> 属性节点下的子节点, 用于初始化二级缓存
          Properties props = context.getChildrenAsProperties();
          // 通过 MapperBuilderAssistant 创建 Cache 对象, 并将其添加到 COnfiguration 中
          builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
        }
      }
    

    其中, type 的对应类型 PERPETUAL

    // PerpetualCache.class 为 org.apache.ibatis.cache.impl.PerpetualCache
    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    

    其他的就是获取属性, 有的有对应的默认值。

    最后需要将这些属性, 通过 MapperBuilderAssistant.useNewCache() 进行缓存设置。

      public Cache useNewCache(Class<? extends Cache> typeClass,
          Class<? extends Cache> evictionClass,
          Long flushInterval,
          Integer size,
          boolean readWrite,
          boolean blocking,
          Properties props) {
        // 建造者模式
        Cache cache = new CacheBuilder(currentNamespace)
            .implementation(valueOrDefault(typeClass, PerpetualCache.class))
            .addDecorator(valueOrDefault(evictionClass, LruCache.class))
            .clearInterval(flushInterval)
            .size(size)
            .readWrite(readWrite)
            .blocking(blocking)
            .properties(props)
            .build();
        // 将对象添加到 configuration 中
        configuration.addCache(cache);
        // 给当前命名空间的缓存成员变量赋值
        currentCache = cache;
        return cache;
      }
    

    该函数创建对应的 Cache 对象, 该对象的 idcurrentNamespace(当前mapper.xml 的 namespace)

      public Cache build() {
        // 设置默认的实现, type 和 lru 对应的类不为空
        setDefaultImplementations();
        // 通过反射创建对象
        Cache cache = newBaseCacheInstance(implementation, id);
        // 根据<cache>节点的子节点<property>, 初始化Cache对象
        setCacheProperties(cache);
        // issue #352, do not apply decorators to custom caches
        // 如果是PerpetualCache类型, 使用 decorators 中的装饰器来包装cache, 并设置属性
        if (PerpetualCache.class.equals(cache.getClass())) {
          for (Class<? extends Cache> decorator : decorators) {
            cache = newCacheDecoratorInstance(decorator, cache);
            setCacheProperties(cache);
          }
          // mybatis 自己提供的标准装饰器
          cache = setStandardDecorators(cache);
        } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
          // 如果不是 LoggingCache 子类, 则添加 LoggingCache 装饰器
          cache = new LoggingCache(cache);
        }
        return cache;
      }
    

    将对象添加到 configuratin 中。

      public void addCache(Cache cache) {
        caches.put(cache.getId(), cache);
      }
    

    对应的成员变量为

      protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
    

    StrictMap 类型。该对象将 namespace 与 缓存对象 Cache 对应起来了。 而 namespacexxxMapper.xml 的标识。

    3 StrictMap

    StrictMap 有什么特殊的地方, 为什么不直接用 HashMap 呢?

    3.1 区别HashMap:键必须为String

    protected static class StrictMap<V> extends HashMap<String, V>
    

    3.2 区别HashMap:多了成员变量 name

    多了一个 name 成员变量, 而且该变量是必须设置的

    所有的构造函数都需要

        public StrictMap(String name, int initialCapacity, float loadFactor) {
          super(initialCapacity, loadFactor);
          this.name = name;
        }
    
        public StrictMap(String name, int initialCapacity) {
          super(initialCapacity);
          this.name = name;
        }
    
        public StrictMap(String name) {
          super();
          this.name = name;
        }
    
        public StrictMap(String name, Map<String, ? extends V> m) {
          super(m);
          this.name = name;
        }
    

    3.3 区别HashMap:key 的处理多了一些变化

    3.3.1 put

        public V put(String key, V value) {
          // 是否存在 key, 存在则直接报异常
          if (containsKey(key)) {
            throw new IllegalArgumentException(name + " already contains value for " + key);
          }
          // 获取 shortKey
          if (key.contains(".")) {
            // 将 key 以 . 分割, 并获取最后一项作为 shortKey
            final String shortKey = getShortName(key);
            if (super.get(shortKey) == null) {
              // 如果 shorKey 对应在 Map 中没有值, 则放入
              super.put(shortKey, value);
            } else {
              // 如果 shorKey 对应在 Map 中有值, 则放入一个 Ambiguity 类
              super.put(shortKey, (V) new Ambiguity(shortKey));
            }
          }
          // key 也会放一个 value
          return super.put(key, value);
        }
    

    3.3.2 shortKey

    关于 shortKey, 其实就是我们以全限定名作为属性时, 它取得是分隔符分割后最后的一项。

    // 将 key 以 . 分割, 并获取最后一项作为 shortKey
    private String getShortName(String key) {
      final String[] keyParts = key.split("\.");
      return keyParts[keyParts.length - 1];
    }
    

    shortKey 它的作用就是类似一个模糊查询的功能, 比如说我们要调用的是 com.mybatis.homejim.mapper.StudentMapper.selectAll 这个函数, 我们可以写

    selectList("com.mybatis.homejim.mapper.StudentMapper.selectAll");
    

    mybatis 中加入 shortKey 之后, 我们只需要写

    selectList("selectAll");
    

    但是, 在实际使用时用处不大, 很多函数基本都是会是二义性的, 不明白为何不取消。

    3.3.3 Ambiguity

    AmbiguityStrictMap 中的静态内部类。

    protected static class Ambiguity {
      final private String subject;
    
      public Ambiguity(String subject) {
        this.subject = subject;
      }
    
      public String getSubject() {
        return subject;
      }
    }
    

    其作用记录存在二义性的 key, 告诉使用者, 你的这个 key 是二义性的。

    3.3.4 get

    
    public V get(Object key) {
      // value 为空则报错
      V value = super.get(key);
      if (value == null) {
        throw new IllegalArgumentException(name + " does not contain value for " + key);
      }
      // 二义性也报错
      if (value instanceof Ambiguity) {
        throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
            + " (try using the full name including the namespace, or rename one of the entries)");
      }
      // 正常情况下应该是返回
      return value;
    }
    

    一起学 mybatis

    你想不想来学习 mybatis? 学习其使用和源码呢?那么, 在博客园关注我吧!!

    我自己打算把这个源码系列更新完毕, 同时会更新相应的注释。快去 star 吧!!

    mybatis最新源码和注释

    github项目

  • 相关阅读:
    python学习笔记-面向对象进阶复习小结
    python学习笔记-类的静态属性,类方法和静态方法
    python学习笔记-面向对象的继承、多态、封装
    python学习笔记-python简介
    python学习笔记-列表、元组字典
    python学习笔记-常用数据类型之字符串
    python学习笔记-函数,递归和内置函数
    python学习笔记-文件操作
    python学习笔记-迭代器与生成器
    python学习笔记-装饰器
  • 原文地址:https://www.cnblogs.com/homejim/p/9743921.html
Copyright © 2011-2022 走看看