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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章