Mybatis 的脉络梳理02之解析Mapper.xml

从上篇可看到,通过 mapper 节点的配置,可指定 Mapper.xml 文件的添加。那么,Mapper.xml 的解析过程又是怎样的呢?

1. Mapper.xml 文件的解析

1.1 通过指定 xml 文件添加 Mapper 映射

  • 代表为通过 resource、url 指定,底层调用 XMLMapperBuilder 类的 parse() 方法
/* resource 相关源码 */
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
/* url 相关源码 */
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
  • parse():通过 XMLMapperBuilder 类解析 mapper.xml 的节点信息,可看到子节点解析为 configurationElement(),点击 2.Mapper.xml 的节点信息解析 查看细节。
/* isResourceLoaded(): 文件是否已解析
 * configurationElement(): 解析 mapper 节点信息
 * addLoadedResource(): 添加 resource 文件为已解析
 * bindMapperForNamespace(): 通过 xml 文件的 namespace 属性绑定 mapper 接口
 * parsePendingResultMaps(): 解析未能正常完成解析的 ResultMap 节点
 * parsePendingCacheRefs(): 解析未能正常完成解析的 CacheRef 节点
 * parsePendingStatements(): 解析未能正常完成解析的 Statement
 * */
public void parse() {
   
  if (!configuration.isResourceLoaded(resource)) {
   
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

1.2 通过指定 Mapper 类添加 Mapper 映射

  • 代表为通过 packageName、class 指定
/* packageName、class 的相关源码 */
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
  • parse(): 通过 MapperAnnotationBuilder 类解析 xml 文件的节点信息
/* loadXmlResource(): 读取 xml 文件资源,底层回到 1.1 的xml文件解析
 * addLoadedResource(): 将资源设置为已解析
 * setCurrentNamespace: 设置当前的命名空间
 * parseCache(): Cache 的注解解析
 * parseCacheRef(): CacheRef 的注解解析
 * parsePendingMethods(): 解析先前解析失败的方法
 */
public void parse() {
   
  String resource = type.toString();
  if (!configuration.isResourceLoaded(resource)) {
   
    loadXmlResource();
    configuration.addLoadedResource(resource);
    assistant.setCurrentNamespace(type.getName());
    parseCache();
    parseCacheRef();
    /* 获取 type 的所有方法,若非桥方法,解析该方法,解析失败添加进 IncompleteMethods */
    Method[] methods = type.getMethods();
    for (Method method : methods) {
   
      try {
   
        // issue #237
        if (!method.isBridge()) {
   
          parseStatement(method);
        }
      } catch (IncompleteElementException e) {
   
        configuration.addIncompleteMethod(new MethodResolver(this, method));
      }
    }
  }
  parsePendingMethods();
}

2. Mapper.xml 的节点信息解析

  • Mapper.xml 解析节点的代码如下
private void configurationElement(XNode context) {
   
  try {
   
    /* namespace 不能为空,否则找不到对应的 Mapper,也就没有意义了 */
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
   
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    /* 设置当前 namesapce,即明确当前解析的是哪个 Mapper 文件 */
    builderAssistant.setCurrentNamespace(namespace);
    /* 以下为 mapper子节点的解析 */
    cacheRefElement(context.evalNode("cache-ref"));
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
   
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}

2.1 cache-ref

  • cache-ref 的 xml 代码示例:通过 namespace 指定一个 Mapper,使用其 cache 配置
<cache-ref namespace<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值