# 第03章 XPathParser

创建好了XMLConfigBuilder后,调用其的parse()方法创建Configuration对象:

 public Configuration parse() {
     if (parsed) {
         throw new BuilderException("Each XMLConfigBuilder can only be used once.");
     }
     parsed = true;
     parseConfiguration(parser.evalNode("/configuration"));
     return configuration;
 }
1
2
3
4
5
6
7
8

其中,方法parser.evalNode("/configuration")调用了XPathParser的evalNode(String expression)的方法,用于解析节点。本章详细讲解XPathParser的创建和XNode的创建过程。

1550317056075

1550319341479

XPathParser在SqlSessionFactoryBuilder创建XMLConfigBuilder的时候创建的:

public XMLConfigBuilder(InputStream inputStream, 
                        String environment, 
                        Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), 
         environment, 
         props);
}
1
2
3
4
5
6
7

XPathParser中:

// XPathParser的构造器
public XPathParser(InputStream inputStream, boolean validation, 
                   Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(inputStream));
}

// 通用构造器
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();
}

// 构建Document
private Document createDocument(InputSource inputSource) {
    // 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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# 3.1 创建EntityResolver

1550325674613

直接传入new XMLMapperEntityResolver():

public interface EntityResolver {
    public abstract InputSource resolveEntity (String publicId,
                                               String systemId)
        throws SAXException, IOException;
}

public class XMLMapperEntityResolver implements EntityResolver {

  private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
  private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
  private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
  private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";

  private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
  private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";

  @Override
  public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
    try {
      if (systemId != null) {
        String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);
        if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {
          return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);
        } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {
          return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);
        }
      }
      return null;
    } catch (Exception e) {
      throw new SAXException(e.toString());
    }
  }

  private InputSource getInputSource(String path, String publicId, String systemId) {
    InputSource source = null;
    if (path != null) {
      try {
        InputStream in = Resources.getResourceAsStream(path);
        source = new InputSource(in);
        source.setPublicId(publicId);
        source.setSystemId(systemId);
      } catch (IOException e) {
        // ignore, null is ok
      }
    }
    return source;
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

1550325776386

主要是将几个文档类型定义(Document Type Definition)文件dtd读入

# 3.2 创建XPath

XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
1
2

使用了工厂+单例模式创建

XPathFactoryImpl中newXPath()方法:

public javax.xml.xpath.XPath newXPath() {
    return new com.sun.org.apache.xpath.internal.jaxp.XPathImpl(
        xPathVariableResolver, xPathFunctionResolver,
        !_isNotSecureProcessing, _useServicesMechanism,
        _featureManager );
}

public class XPathImpl implements javax.xml.xpath.XPath {...}
1
2
3
4
5
6
7
8

其中XPathImpl实现了javax.xml.xpath.XPath接口,提供了获取XPath表达式的途径,而XPath提供了XPath的求值环境和表达式,用于在XML文档中通过元素和属性进行导航,并对元素和属性进行遍历。

# 3.3 创建Document

// 构建Document
private Document createDocument(InputSource inputSource) {
    // 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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

上面代码的5~11行用于设置DocumentBuilderFactory中参数的含义:

  • setValidating表示是否验证xml文件,这个验证是DTD验证
  • setNamespaceAware表示是否支持xml命名空间
  • setIgnoringComments表示是否忽略注释
  • setIgnoringElementContentWhitespace表示是否忽略元素中的空白
  • setCoalescing表示是否将CDATA节点转换为Text节点,并将其附加到相邻(如果有)的Text节点
  • setExpandEntityReferences表示是否扩展实体引用节点

第13行的代码由设置的参数从DocumentBuilderFactory中获取一个DocumentBuilder实例DocumentBuilderImpl,并由第14行的代码设置一个实体解析器,由第15行~第29行的代码设置一个错误处理器。

而第30行DocumentBuilder的parse()方法:

public Document parse(InputSource is) throws SAXException, IOException {
    if (is == null) {
        throw new IllegalArgumentException(
            DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
                                              "jaxp-null-input-source", null));
    }
    if (fSchemaValidator != null) {
        if (fSchemaValidationManager != null) {
            fSchemaValidationManager.reset();
            fUnparsedEntityHandler.reset();
        }
        resetSchemaValidator();
    }
    domParser.parse(is);
    Document doc = domParser.getDocument();
    domParser.dropDocumentReferences();
    return doc;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

使用DocumentBuilder将解析InputSource成org.w3c.dom.Document并将Document存储到XPathParser中.

综上所述:

XMLConfigBuilder会将XML配置文件的信息转换为Document对象,而XML配置定义文件DTD转换成XMLMapperEntityResolver对象,然后将二者封装到XpathParser对象中,XpathParser的作用是提供根据Xpath表达式获取基本的DOM节点Node信息的操作。如下图所示:

1550329847925

1550329940848

之后XMLConfigBuilder调用parse()方法:会从XPathParser中取出<configuration>节点对应的Node对象,然后解析此Node节点的子Node:properties, settings, typeAliases,typeHandlers, objectFactory, objectWrapperFactory, plugins, environments,databaseIdProvider, mappers

 public Configuration parse() {
     if (parsed) {
         throw new BuilderException("Each XMLConfigBuilder can only be used once.");
     }
     parsed = true;
     parseConfiguration(parser.evalNode("/configuration"));
     return configuration;
 }

/**
 * 解析 "/configuration"节点下的子节点信息,然后将解析的结果设置到Configuration对象中
 */
private void parseConfiguration(XNode root) {
    try {
        //1.首先处理properties 节点
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        //2.处理typeAliases
        typeAliasesElement(root.evalNode("typeAliases"));
        //3.处理插件
        pluginElement(root.evalNode("plugins"));
        //4.处理objectFactory
        objectFactoryElement(root.evalNode("objectFactory"));
        //5.objectWrapperFactory
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        //6.settings
        settingsElement(settings);
        //7.处理environments
        environmentsElement(root.evalNode("environments"));
        //8.database
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        //9.typeHandlers
        typeHandlerElement(root.evalNode("typeHandlers"));
        //10 mappers
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

具体解析过程见下一章

Last Updated: 10/20/2019, 11:49:45 PM