Mybatis原码学习(二)基础支持层:解析器模块

在Mybatis中设计到多个xml文件,就得接触到xml解析的内容。

xml解析常见的方式有三种,DOM,SAX,StAX

DOM:

树型结构,搜索快,添加修改也快,找自己的关系节点也快,但是呢在运行的时候需要把整个XML文档放入内存当中,搭建树形结构,所以如果xml中的数据量比较大,就会造成较大的资源浪费。

SAX:

流式处理,只将一部分xml文档加载到内存中,资源占用较小,而且程序处理模式就像深搜一样,达成自己的目的就立即退出,不必解析剩余的。
解析节点的时候出发节点上面的回调函数,回调函数是由开发人员自己注册的。这种方式也被称为“推模式”。
SAX的确实就是由于不储存XML文档,所以需要开发人员自己去维护节点的关系,工作量比较大。然后是流式处理,只能从前往后单向处理。无法支持XPATH。

StAX:

是JKD提供的解析XML的API,和SAX类似,也是流式处理,但是StAX采用的是“拉模式”,应用程序控制着整个解析的过程,也就是“用到的时候再去找”。
提供了两套API,一套是指针的,一套迭代器的。

XPath

之前学爬虫的时候用过,就是通过一些匹配方式去匹配HTML文档中的节点,类似正则表达。,那么对于XML文档,配合着DOM解析方式使用,就能实现对XML文档的解析,也是同样的道理。
XPathParser
Mybatis提供的XPathParser封装了XPath、Document、EntityResolver,

XpathParser中各个字段的含义和功能:

  private Document document; //Document文本对象
  private boolean validation;//是否开启验证
  private EntityResolver entityResolver;//用于加载本地DTD文件
  private Properties variables;//properties中的键值对集合
  private XPath xpath;//xpath对象

开始对XML文档进行验证的时候,会根据XML文档开始位置指定的网址加载对应的DTD或者XSD文件,

但是如果网太慢了就会导致验证过程太慢,所以就需要提前设置EntityResolver接口对象加载本地的DTD文件。
XMLMapperEntityResolver是Mybatis提供的EntityResolver接口实现类。代码挺长就不放来了,我总结的这个实现类干的事情就是 :
通过匹配SystemId指定的是哪个DTD文档,去通过getInputSource()把该DTD转化成InputSource对象。

解决完EntityResolver之后呢,XPthParse中的createDocument()方法封装了创建Document对象的过程,并且出发了加载XML文档的过程:

//初始化
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }

private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor 这里是让用这个方法之前一定要用上面的初始化一下
    try {
	 //创建DBF对象
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);
	//创建DB对象
      DocumentBuilder builder = factory.newDocumentBuilder();
	  //设置EntityResolver接口对象
      builder.setEntityResolver(entityResolver);
	  //空实现的异常处理
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
	  //加载XML文档
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

然后就是XPth部分了。
在XPathParser中提供了一系列的eval*()方法用于解析不同boolean、short、long、int、string等类型的信息,代码都比较简单,如int:

  public Integer evalInteger(String expression) {
    return evalInteger(document, expression);
  }

  public Integer evalInteger(Object root, String expression) {
    return Integer.valueOf(evalString(root, expression));
  }

值得一提的是XPathParser.evalString()方法,其中会调用PropertyParser.parse()方法处理节点中的键值对映射(把key转换成value)。
这个方法里面会创建一个GenericTokenParser解析器,这个解析器里面有三个字段

  private final String openToken;
  private final String closeToken;
  private final TokenHandler handler;

从上到下分别是占位符开始标记,占位符结束标记,实现的Tokenhandler用于解析占位符。
比如 开 始 标 记 就 是 “ { } 开始标记就是“ {”,结束标记就是“}”,然后handler就是把键值对里面的key换成value。
GenerpicTokenParser.parse()整个算法的流程就是这样的:
通过开始标记和结束标记去确定原字符串里面需要替换的地方,然后通过Tokenhandler把key换成value,再按原来的顺序把字符串拼接好。
比如传过来的字符串是"‘name’: u s e r n a m e , ′ p a s s w o r d ′ : {user_name},'password': username,password:{pw}",然后我们的键值对关系是"user_name":“lyc”,“pw”:“123”,
那么经过这个parse()方法之后,原字符串就变成了:"‘name’:lyc,‘password’:123"。

占位符的解析是通过TokenHandler接口实现的,所以整个过程中的具体解析行为可以根据持有的TokenHandler的不同而有所不同。

XNode

最后呢,还有一个XNode,它是对org.w3c.dom.Node对象的封装和解析。

  private Node node;//org.w3c.dom.Node对象
  private String name;//Node节点对象
  private String body;//节点的内容
  private Properties attributes;//节点属性集合
  private Properties variables;//键值对
  private XPathParser xpathParser;//这就是前面介绍的xpathParser

XNode中的构造函数会调用parseAttributes()和parseBody()方法解析Node对象中的信息,对attributes和body字段初始化,另外呢,XNode中提供了多种get*()方法获取所需的节点信息,也实现了XPthParser对象的eval*()方法。

就到这里啦,也算挺有收获的今天。

你可能感兴趣的:(Mybatis)