mybatis源码解析 xml解析模块

通过mybatis源码解析 IO读取文件模块 了解到mybatis在开始干活前,首先要读取xml文件。通过Resources类将xml文件转为相应的数据流
本篇文章就是处理这些流,将这些数据流映射到XmlParserState类中,后面的操作就是对该XmlParserState类进行操作。解析xml文件到对象中有三个类关系是非常密切的,Nodelet,SqlMapConfigParser,NodeletParser类。
在这里插入图片描述
Nodelet接口有一个方法process,由SqlMapConfigParser 形如addSqlMapConfigNodelets()等方法实现。官方称为匿名类。

如下的代码,parser.setValidation和parser.setEntityResolver先不用关心。

public SqlMapConfigParser() {
  parser.setValidation(true);
  parser.setEntityResolver(new SqlMapClasspathEntityResolver());
  
  addSqlMapConfigNodelets();
  addGlobalPropNodelets();
  addSettingsNodelets();
  addTypeAliasNodelets();
  addTypeHandlerNodelets();
  addTransactionManagerNodelets();
  addSqlMapNodelets();
  addResultObjectFactoryNodelets();
}

形如addSqlMapConfigNodelets的方法中,干了两件事情:1 调用NodeletParser中的addNodelet方法将路径 和 实例化Nodelet对象加入到Map集合中;2 实现Nodelet接口的process方法。

private void addSqlMapConfigNodelets() {
  parser.addNodelet("/sqlMapConfig/end()", new Nodelet() {
    public void process(Node node) throws Exception {
      state.getConfig().finalizeSqlMapConfig();
    }
  });
}

这里的addSqlMapConfigNodelets()调用后,实例化Nodelet对象,并且实现Nodelet接口中的process方法。 (下面的NodeletParser方法做了删减)。
下面的process方法处理xml树,采用的是类似二叉树的前序遍历方法。先从根节点开始,然后遍历左孩子,左孩子遍历后如果发现左孩子还有孩子再继续遍历他的孩子,直到孩子都被遍历完为止。孩子被遍历完后再遍历这个左孩子的兄弟。

private void process(Node node, Path path) {
  //处理当前结点
  String elementName = node.getNodeName();
  
  processNodelet(node, path.toString());
  processNodelet(node, new StringBuffer("//").append(elementName).toString());

  //处理当前结点的中的
  NamedNodeMap attributes = node.getAttributes();
  int n = attributes.getLength();
  for (int i = 0; i < n; i++) {
	
  }
  
  //当前结点的孩子结点,如果此孩子结点还有孩子,再继续递归处理孩子的孩子。
  //可以理解是:发现结点有孩子,就要把这颗子树的孩子遍历完
  NodeList children = node.getChildNodes();
  for (int i = 0; i < children.getLength(); i++) {
	process(children.item(i), path);
  }
  
}
//processNodelet方法调用Nodelet接口中的process方法
private void processNodelet(Node node, String pathString) {
	Nodelet nodelet = (Nodelet) letMap.get(pathString);
	//调用接口中的process方法
	//注意不是此类中的process,此方法已经在SqlMapConfigParser中已经实例化。
	nodelet.process(node);
}

实例化Nodelet接口中的process方法是将sqlMapConfig结点包含信息进行处理,然后把这些信息实例化给XmlParserState对象,比如处理如下xml结点

<properties resource="com/ibatis/sqlmap/maps/SqlMapConfig.properties"/>

调用processNodelet方法,此方法找到对应已经实例化Nodelet对象,Nodelet接口对象调用process方法。根据prpperties文件路径,把properties文件信息加载XmlParserState信息中去。关于XmlParserState是干什么的,下文两个标题有详细解释。整个NodeletParser处理目的,就是把xml内容赋给这个XmlParserState对象。
在这里插入图片描述

解释XmlParserState的方法意义。


sqlMapConfigParser 和 NodeletParser 和 Nodelet 接口的关系。

明白一点,sqlMapConfigParser 的构造方法往letMap中添加Nodelet初试化的对象,同时Nodelet接口实例化后,接口对象里process的方法不首先执行,只有实例化相应对象被调用才执行。NodeletParser中的letMap的map中存放着key:xpath和value:Nodelet对象。NodeletParser 中的processNodelet方法,根据比较letMap中的key选择相应Nodelet的实例对象调用被实现的process。

process就能调用对象的process方法了。
这里就可以为接口的多态应用都是同一个Nodelet接口,Nodelet.process进入不同实现类中的process方法。同时new Nodelet()已经构造除了对象了,无需再进行new了。


XmlParserState是干什么的。

sqlMapConfigParser 和 NodeletParser 和 Nodelet 最终目的是读取SqlMapConfig.xml文件,然后把文件的内容
灌到 XmlParserState 对象中去,然后拿着 XmlParserState 让他去调用sql执行,缓存,返回结果映射,事务管理等模块。
理解了先后关系,这样一步一步就可以很好的分析代码了。



该文章的代码已经在git中有提交,可以通过BatchTest调试跟踪。
https://gitee.com/cnhellorui/some_source_code/tree/master/mybatis_sourcecode_analysis/ibatis-2

水平原因可能存在错误,欢迎指出。谢谢! email: chenrui@marsdl.com

展开阅读全文

没有更多推荐了,返回首页