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;
}
<think>好的,我现在需要帮助用户找到MyBatis 3 Mapper的DTD文件的位置或内容。首先,用户提到的引用[1]中提到了在Eclipse中创建MyBatis相关的XML文件时需要包含特定的DOCTYPE声明,比如: `<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">` 这说明用户已经知道DTD的公共标识符和URL,但可能想确认这个DTD的具体内容或者本地存储位置。 接下来,我需要回忆MyBatis的文档结构。通常,DTD文件用于定义XML文档的结构。MyBatisMapper XML文件遵循特定的DTD,这个DTD可能托管在mybatis.org的服务器上,供在线访问。但用户可能想知道是否有本地的副本,或者如何获取DTD的内容。 查看其他引用,比如引用[3]和[4]提到了MyBatis的配置和生成器,但并没有直接涉及DTD的内容。引用[5]讨论了mapper标签的配置和解析过程,可能涉及DTD的结构,但具体内容还是需要直接查看DTD文件。 可能的解决步骤包括: 1. 直接访问提供的URL:http://mybatis.org/dtd/mybatis-3-mapper.dtd,查看是否能下载或查看DTD内容。 2. 检查MyBatis的官方文档或源码仓库,是否有DTD文件的定义。 3. 如果用户需要在本地配置DTD,以便在IDE中验证XML,可能需要下载该DTD并配置到Eclipse或其他IDE中,如引用[1]所示。 根据经验,MyBatis的DTD文件通常托管在官方网站,用户可以直接通过URL访问。此外,MyBatis的GitHub仓库可能也包含这些DTD文件。需要确认该URL是否有效,并检查内容是否符合预期。 另外,考虑到用户可能在使用Eclipse时需要本地DTD文件来避免网络请求,可以建议用户将DTD文件下载到本地,并在IDE中配置路径,这样在编写XML时就不会依赖网络连接。引用[1]提到的方法可能涉及在Eclipse中设置DTD的本地位置。 总结,用户的问题可以通过直接访问提供的URL获取DTD内容,或者从MyBatis源码仓库中找到相关定义。此外,指导用户如何在IDE中配置本地DTD引用也是一个有用的信息点。</think>### MyBatis 3 Mapper DTD 文件位置及内容 MyBatis 3 Mapper DTD 文件用于定义 Mapper XML 文件的结构,确保标签和属性的合法性。以下是其位置和内容获取方式: 1. **在线访问 DTD 文件** 通过公共标识符或直接 URL 访问: ```xml <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> ``` - 直接访问 URL:[http://mybatis.org/dtd/mybatis-3-mapper.dtd](http://mybatis.org/dtd/mybatis-3-mapper.dtd) 可查看完整的 DTD 定义[^1]。 2. **本地配置 DTD(适用于 IDE 离线验证)** 若需在 Eclipse 等 IDE 中离线使用,可按以下步骤操作: - 下载 DTD 文件:从上述 URL 保存 `mybatis-3-mapper.dtd` 到本地目录。 - 配置 Eclipse: 1. 打开 `Window > XML > XML Catalog`。 2. 添加新的条目,关联公共标识符 `-//mybatis.org//DTD Mapper 3.0//EN` 与本地文件路径。 3. **DTD 核心内容(节选)** ```dtd <!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select* )+> <!ATTLIST mapper namespace CDATA #IMPLIED > <!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*> <!ATTLIST select id CDATA #REQUIRED parameterType CDATA #IMPLIED resultType CDATA #IMPLIED ... > ``` 完整内容需通过上述 URL 查看。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值