mybatis源码解析一 xml解析(解析器)

     最近闲来无事,看着一些源码类的书籍,只是光看,好像并不能给自己很好的益处,无法记下来,所以就有了这个Mybatis源码解析系列的博客。网上也有大量的源码解析,在此记录有两个原因,一是为了加深自己的印象,二来则是让广大读者朋友能及时纠正在下的一些理解的错误。如果各位发现有任何的错误,请留言指出,在此感激不尽。闲话不多说,这就开始我们的第一篇xml解析。
     首先,本博客系列的图、代码,大多来自《mybatis技术内幕》一书。

     我们先来看看如图,mybatis的结构分层。
mybatis源码解析一 xml解析(解析器)_第1张图片

这一讲主要是对解析器模块一个简单分析。

那么就先由一个简单的Mybatis启动开始讲解吧。
也请大家下载mybatis源码(或者在https://github.com/mybatis/mybatis-3中找到对应的源码),本博客中的例子大部分来自己mybatis中的test包。我尽量减少在里面放置过多的源码。


https://github.com/mybatis/mybatis-3/blob/master/src/test/java/org/apache/ibatis/session/SqlSessionTest.java#L64


  public static void setup() throws Exception {
    createBlogDataSource();
    final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
    final Reader reader = Resources.getResourceAsReader(resource);
    sqlMapper = new SqlSessionFactoryBuilder().build(reader);
  }

在此处加载了mybatis的config文件,大家在mapperConfig.xml(https://github.com/mybatis/mybatis-3/blob/master/src/test/java/org/apache/ibatis/builder/MapperConfig.xml)中可以看到内容的。

在Build方法调用时会创建相应的xpath对象

mybatis源码解析一 xml解析(解析器)_第2张图片

https://github.com/mybatis/mybatis-3/blob/master/src/main/java/org/apache/ibatis/parsing/XPathParser.java#L229
createDocument方法中创建了xpath。

现在我们来说说xpath的API解析XML。

package org.apache.ibatis.test;

import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.io.Resources;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.Reader;

public class XPathTest {

    private Document createDocument(InputSource inputSource) {
        boolean validation = true;
        EntityResolver entityResolver = new XMLMapperEntityResolver();
        // important: this must only be called AFTER common constructor
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setValidating(validation);

            factory.setNamespaceAware(false);
            factory.setIgnoringComments(true);
            factory.setIgnoringElementContentWhitespace(false);
            factory.setCoalescing(false);
            factory.setExpandEntityReferences(true);

            DocumentBuilder builder = factory.newDocumentBuilder();
            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 {
                }
            });
            return builder.parse(inputSource);
        } catch (Exception e) {
            throw new BuilderException("Error creating document instance.  Cause: " + e, e);
        }
    }

    public static void main(String[] args) throws IOException, XPathExpressionException {
        final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
        final Reader reader = Resources.getResourceAsReader(resource);
        XPathTest test = new XPathTest();
        Document document = test.createDocument(new InputSource(reader));
        XPathFactory factory = XPathFactory.newInstance();
        XPath xPath = factory.newXPath();
        Node nodes = (Node) xPath.evaluate("/configuration", document, XPathConstants.NODE);
        // 获取指定节点
        Node mappers = (Node) xPath.evaluate("mappers", nodes, XPathConstants.NODE);
        NodeList mapperList = mappers.getChildNodes();
        for(int i=0; i 0)
                parse(childNodes);
        }
    }

    public static String getAttribute(Node node, String attributeName){
        NamedNodeMap nodeAttributes = node.getAttributes();
        for (int i= 0; i< nodeAttributes.getLength(); i ++){
            Node attribute = nodeAttributes.item(i);
            String name = attribute.getNodeName();
            String value = attribute.getNodeValue();
            if(attributeName.equalsIgnoreCase(name)){
                return value;
            }
        }
        return null;
    }
}

这是一个简单的xpath的解析xml的例子,与Mybatis中获取xml配置的解析差不多。
由此我们知道了xml的各节点是以该方式解析出来的。

现在我们来看看XPathParse类,该类就是封装了解析xml的api的。
XPathParse类中的字段说明:

private final Document document;  //就是我们上述XPathTest中的document对象
private boolean validation; //是否开启验证
private EntityResolver entityResolver;//用于加载本地的DTD文件(DTD是用来验证xml的合法性的)
private Properties variables; //用于记录mybatis-config.xml中的变量的键值对的,比如在xml中有${password}等变量时,会由该字段保留具体值。
private XPath xpath;// 与XPathTest中的xpath一致,用于解析xml

mybatis源码解析一 xml解析(解析器)_第3张图片

XNode对象是对于XPath中的获取的Node对象的一个封装,这里就不过多的讲解,各位可以去看该类的源码。

在XMLConfigBuilder类中的https://github.com/mybatis/mybatis-3/blob/master/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java#L102
 

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }


此方法中的root.evalNode中获取的XNode对象就是对XPath中的Node对象的一个封闭。

在此xml解析就大致完了,下一节应该会是反射相关的分析。
mybatis中大量用了反射来将Mapper接口中的方法参数与返回值做解析。

你可能感兴趣的:(mybatis源码系列,源码)