【Mybatis】【配置文件解析】【四】Mybatis源码解析-mappers的解析四(绑定Mapper、处理未解析全的)

1  前言

我们上节把我们 mapper 里的 sql节点以及我们的增删改查都解析了,那么最后回来就剩下两块没看了,一块是我们的 mapper 跟我们的接口绑定,一块就是我们在解析的过程中报错的会放进未完全的集合中,这里来做最后的努力。那我们来看下这两块的东西。

2  源码分析

2.1  接口绑定 Mapper

从我们的 bindMapperForNamespace() 方法开始看起:

private void bindMapperForNamespace() {
  // 获取我们 Mapper 上的名称空间 我们不都是写的接口的全类名么
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      // 根据全类名得到我们的 Mapper 接口
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      // ignore, bound type is not required
    }
    // 检测当前 mapper 类是否被绑定过
    if (boundType != null && !configuration.hasMapper(boundType)) {
      // Spring may not know the real resource name so we set a flag
      // to prevent loading again this resource from the mapper interface
      // look at MapperAnnotationBuilder#loadXmlResource
      configuration.addLoadedResource("namespace:" + namespace);
      // 绑定 mapper 类
      configuration.addMapper(boundType);
    }
  }
}
// Configuraion
public <T> void addMapper(Class<T> type) {
  // 通过 MapperRegistry 绑定 mapper 类
  mapperRegistry.addMapper(type);
}
public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      /*
       * 将 type 和 MapperProxyFactory 进行绑定,
       * MapperProxyFactory 来为 mapper 接口生成代理类
       */
      knownMappers.put(type, new MapperProxyFactory<>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      // 创建注解解析器。在 MyBatis 中,有 XML 和 注解两种配置方式可选
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      // 解析注解中的信息
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

可以看到它创建代理,只是把我们的 mapper 接口作为属性包了一层,并没有创建真实的对象,至于什么时候创建,我们后续再说哈,我们解析阶段先只看静态的东西哈。对于Mapper 接口的绑定过程。这里简单一下:

  1. 获取命名空间,并根据命名空间解析 mapper 类型;
  2. 将 type 和 MapperProxyFactory 实例存入 knownMappers 中;
  3. 创建注解解析器进行解析(这个就不看了,注解无非也是拿到我们的注解的 sql 最后都是字符串 然后进行解析的。

2.2  处理未完整的对象

我们在解析过程中,会看到报错捕获到就会把它放进未完整的集合里,放的都是什么呢,都是不同的解析器对象,不知道你注意到没。在解析 resultMap时,有对应的 ResultMapResolver,解析 cache-ref 有对应的 CacheRefResolver,存放的都是其对用的解析器。出现未完整的对象的一个原因可能是每个 mapper 的加载顺序的不固定,比如会依赖某个 mapper 的东西但是对应的 mapper 还未加载,就会出现这样的情况。其实这三个的处理基本都是一样的,我们看其中一个:

private void parsePendingCacheRefs() {
  // 获取 CacheRefResolver 列表
  Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
  synchronized (incompleteCacheRefs) {
    // 迭代器遍历
    Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
    while (iter.hasNext()) {
      try {
        // 尝试再次解析
        iter.next().resolveCacheRef();
        // 解析成功删除自己,继续下一个
        iter.remove();
      } catch (IncompleteElementException e) {
        // 不做处理
        // Cache ref is still missing a resource...
      }
    }
  }
}

3  小结

好那么到这里为止,我们的 mapper 的解析过程就结束了,我们的配置解析也就结束了,有不对的地方欢迎指正哈。

posted @ 2023-02-28 21:22  酷酷-  阅读(42)  评论(0编辑  收藏  举报