MyBatis源码解析----parsing包

一.接口和类介绍

1.TokenHandler

public interface TokenHandler {
  String handleToken(String content);
}

这个接口中只有一个函数,就是对字符串进行处理。将db.properties中参数中占位符${xxx}进行参数替换

实现类

private static class VariableTokenHandler implements TokenHandler 

解析形如:“username:default” 字符串 ,先获取key(username),然后从配置集合中查询对应的value,并返回value的值。
如果集合variables 集合不存在,返回 ${content}

private static class VariableTokenHandler implements TokenHandler {
    private final Properties variables;
    private final boolean enableDefaultValue;
    private final String defaultValueSeparator;

    //构造函数初始化 handle
    private VariableTokenHandler(Properties variables) {
      this.variables = variables;
      //字符串转化为false/true,初始化值为“true”或者“false” 是字符串
      this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
      //获取分隔符号,DEFAULT_VALUE_SEPARATOR 在外部类中定义了
      this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
    }

    private String getPropertyValue(String key, String defaultValue) {
      return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
    }
    @Override
    // 解析key,根据key返回 variables 中的 value
    public String handleToken(String content) {
      //content=db.username:postgres
      //1.按照分割符分割指定的占位符号,得到占位符号的名称和默认值
      //然后按照分割得到的占位符号名称找到对应的值
      // 如果在properties 中未定义对应的键值对,则切分的得到的默认值作为解析结果返回
      if (variables != null) {//检测集合是否为空
          //isEmpty() 用于判断List内容是否为空,即表里一个元素也没有,
          //list等于null,可理解为没有对list集合分配内存空间,实际上压根就不存在
          //System.out.println("if (variables != null)");
        String key = null;// 意义何在?
        if (enableDefaultValue) {//判断是否开启了占位符中使用默认值
          final int separatorIndex = content.indexOf(defaultValueSeparator);
          //查找分隔符号
          String defaultValue = null;
          if (separatorIndex >= 0) {
            //key
            key = content.substring(0, separatorIndex);//key
            //默认值value
            defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
          }
          if (defaultValue != null) {
            //查找指定的key 的value是否存在,不存在则为默认值
            return variables.getProperty(key, defaultValue);
          }
        }
        //如果在集合中存在key,返回对应的值k
       if (variables.containsKey(key)) {
          return variables.getProperty(key);
        }
      }
      //如果参数集合为null,则返
      //我觉得,这样做没什么实际意
      return "${" + content + "}";
    }
  }

2.GenericTokenParser 一般意义上的字符串解析

字符串处理:形如“www.${name:default}.sankuai.com” 提取出”name:default” 并用xule05 代替 ${name:default}
最终返回的字符串为:www.xule05.sankuai.com

package org.apache.ibatis.parsing;

/**
 * @author Clinton Begin
 */
public class GenericTokenParser {

  private final String openToken;//占位符开始 ${
  private final String closeToken;//占位符结束 }
  private final TokenHandler handler;// 解析占位符 key:value
  ......
  //GenericTokenParser中唯一方法
  //例如解析www.${name:default}.sankuai.com
public String parse(String text) {//获取节点的文本属性
      //1.将” ${ “之前的字符串添加到builder中
       ......
      //2.将解析到的字符串添加到builder,参数填充委托给handle处理
      builder.append(handler.handleToken(expression.toString()));
      //3. 将“}”之后的字符串添加到builder
      ......
      return builder.toString();
}

简单的说,这个函数的作用就是将openToken和endToken间的字符串取出来用handler处理下,去掉”${” 和”}“然后再拼接到一块。我们接下来看一个具体的handler,了解下它对传入的字符串做了怎样的处理。

package org.apache.ibatis.parsing;

/**
 * @author Clinton Begin
 */
public class GenericTokenParser {

  private final String openToken;//占位符开始${
  private final String closeToken;//占位符结束}
  private final TokenHandler handler;// 占位符处理器:解析占位符 key:value
   //构造函数初始化上述三个字段
  public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
    this.openToken = openToken;
    this.closeToken = closeToken;
    this.handler = handler;
  }
  //按照顺序查找openToken closeToken 解析得到占位符号,交给handle处理
  //将解析结果重新拼装为字符串返回
  //text === ${key:defaultvalue}
  //我觉得这个方法考虑的太复杂了
  public String parse(String text) {
    if (text == null || text.isEmpty()) {
      return "";
    }
    // search open token
    int start = text.indexOf(openToken, 0);//从指定位置查找出现的字符串  等同于 indexOf(openToken)
    if (start == -1) {
      return text;
    }
    char[] src = text.toCharArray();
    int offset = 0;//定义的偏移量很经典,值得学习!!!用一个偏移量解决了
    // 使用final 的原因是???
    final StringBuilder builder = new StringBuilder();//用来记录解析后的字符串
    StringBuilder expression = null;//用来记录一个占位符的字符串
    while (start > -1) {//为什么要用while 循环
      if (start > 0 && src[start - 1] == '\\') {
        // this open token is escaped. remove the backslash and continue.
        // 可能字符串开始为 \\$  先删除反斜线,继续解析字符串
        //这个开放的标记被转义。 删除反斜杠并继续
        //遇到转义的字符串,直接将前面的字符串以及开始标记追加到builder中
        // "builderpath\\${url:www.baidu.com\\}"
        builder.append(src, offset, start - offset - 1).append(openToken);
        //builderpath${
        offset = start + openToken.length();// offset 指向url中第一个字母
      } else {
        //查找到开始标记,而且未被转义
        // found open token. let's search close token.
        // 开始标记已经找到,寻找结束标记
        if (expression == null) {//为什么不直接赋值???
          expression = new StringBuilder();
        } else {
          expression.setLength(0);//设置为0 有事什么意识???
        }
        //将前面的字符串追加到builder中
        builder.append(src, offset, start - offset);
        offset = start + openToken.length();
        //查找结束标记
        int end = text.indexOf(closeToken, offset);
        while (end > -1) {
          if (end > offset && src[end - 1] == '\\') {
            // this close token is escaped. remove the backslash and continue.
            expression.append(src, offset, end - offset - 1).append(closeToken);
            offset = end + closeToken.length();
            end = text.indexOf(closeToken, offset);
          } else {
            expression.append(src, offset, end - offset);
            offset = end + closeToken.length();
            break;
          }
        }
        if (end == -1) {
          // close token was not found.
          builder.append(src, start, src.length - start);
          offset = src.length;
        } else {
          //调用处理器处理字符串函数
          builder.append(handler.handleToken(expression.toString()));
          offset = end + closeToken.length();
        }
      }
      start = text.indexOf(openToken, offset);
    }
    if (offset < src.length) {
      builder.append(src, offset, src.length - offset);
    }
    //System.out.println(builder.toString());
    return builder.toString();
  }

3.PropertyParser 从类的名称上看是 参数(Property)解析(Parser)

//核心方法
public static String parse(String string, Properties variables) {
    //先初始化一个handler
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    //在初始化GenericTokenParser对象,设置openToken为${,endToken为}
    //有没有对${}比较熟悉,这个符号就是mybatis配置文件中的占位符,例如定义datasource时用到的 <property name="driverClassName" value="${driver}" />
    //同时也可以解释在VariableTokenHandler中的handleToken时,如果content在properties中不存在时,返回的内容要加上${}了。
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    //返回一个不带${key}且ke被正确解析的字符串
    return parser.parse(string);
}
public class PropertyParser {

  private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
  //指示是否在占位符上启用默认值的特殊属性键。默认值为false,表示在占位符上禁用默认值
  //如果指定了true,则可以在占位符上指定键和默认值(例如$ {db.username:postgres}
  public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";
  private static final String ENABLE_DEFAULT_VALUE = "true";
  //为占位符指定键和默认值的分隔符的特殊属性键。
  public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";
  // 分隔符号为 :
  private static final String DEFAULT_VALUE_SEPARATOR = ":";
  //================
  // 第一个键值对:
  //指示是否在占位符上启用默认值的特殊属性键。默认值为false,表示在占位符上禁用默认值
  //如果指定了true,则可以在占位符上指定键和默认值(例如$ {db.username:postgres}
  // key=org.apache.ibatis.parsing.PropertyParser.enable-default-value
  // value=false(默认)禁用默认值

  //第二个键值对:
  //为占位符指定键和默认值的分隔符的特殊属性键。
  //key=org.apache.ibatis.parsing.PropertyParser.default-value-separator
  //value=: (默认值)
  private PropertyParser() {
    // Prevent Instantiation
  }
  //核心方法
  public static String parse(String string, Properties variables) {
    //生产一个handle
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    return parser.parse(string);
  }
  //解析并返回默认值,持有variableTokenHandler参数集合的引用
  private static class VariableTokenHandler implements TokenHandler {
    private final Properties variables;
    private final boolean enableDefaultValue;
    private final String defaultValueSeparator;

    //构造函数初始化 handle
    private VariableTokenHandler(Properties variables) {
      this.variables = variables;
      //字符串转化为false/true
      this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
      //获取分隔符号
      this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
    }

    private String getPropertyValue(String key, String defaultValue) {
      return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
    }


    //返回key的在variables中值,variables==null ,返回${key}
    @Override
    public String handleToken(String content) {
      //content=db.username:postgres
      //1.按照分割符分割指定的占位符号,得到占位符号的名称和默认值
      //然后按照分割得到的占位符号名称找到对应的值
      // 如果在properties 中未定义对应的键值对,则切分的得到的默认值作为解析结果返回
      if (variables != null) {//检测集合是否为空
          //isEmpty() 用于判断List内容是否为空,即表里一个元素也没有,
          //list等于null,可理解为没有对list集合分配内存空间,实际上压根就不存在
          //System.out.println("if (variables != null)");
        String key = null;// 意义何在?
        if (enableDefaultValue) {//判断是否开启了占位符中使用默认值
          final int separatorIndex = content.indexOf(defaultValueSeparator);
          //查找分隔符号
          String defaultValue = null;
          if (separatorIndex >= 0) {
            //key
            key = content.substring(0, separatorIndex);//key
            //默认值value
            defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
          }
          if (defaultValue != null) {
            //查找指定的key 的value是否存在,不存在则为默认值
            return variables.getProperty(key, defaultValue);
          }
        }
        //如果在集合中存在key,返回对应的值k
       if (variables.containsKey(key)) {
          return variables.getProperty(key);
        }
      }
      //如果参数集合为空,则返
        //System.out.println("${" +1243+ "}");
      return "${" + content + "}";
    }
  }
public  static  void main(String[] args){
    Properties variables =new Properties();
    variables.put("key","2016");
    variables.put("密码","123gsycgjabc");
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    String s = handler.handleToken("key:value");
    System.out.println(s);
    String parse = parser.parse("xule${密码:1234455555}");
    System.out.println(parse);
}
}

输出结果差异:
handle.handleToken
GenericTokenParser.parser

2016
xule123gsycgjabc

4.XPathParser
属性

  private final Document document;//Document对象
  private boolean validation;//是否开启验证
  private EntityResolver entityResolver;//加载本地DTD文件
  private Properties variables;//mybatis-config.xml 中的 properties 标签下的键值对集合
  private XPath xpath;//XML 查询对象

打开这个类的outline会发现这个类包含的函数真的是“蔚为壮观”,虽然数量众多,基本上可以分为两类:初始化(构造函数)、evalXXX。

4.1初始化:
构造多达10几个XPathParser类的构造函数数量众多,是由于这个类的属性比较多,这些构造函数内部都会调用到如下函数:commonConstructor和createDocument。接下来我们来看看这两个函数具体做了那些事情:

//注意:这是一个非构造方法:为什么不使用构造器,会被其他构造器调用
private void commonConstructor(boolean validation, Properties variables,       EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    //利用XPathFactory创建一个新的xpath对象
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }
// 看这个commo怎么被调用的
  public XPathParser(String xml) {
    commonConstructor(false, null, null);
    this.document = createDocument(new InputSource(new StringReader(xml)));
  }

加mybatis-config.xml并创建文档Document

private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    // 重要:这只能在公共构造函数后调用
    try {
      //1.文档创建者工厂
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);
      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);
      //2.文档创建者
      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 {
        }
      });
      //3.创建doc文档
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

4.2 evalXXX

这个类中的evalXXX函数有两种多态形式:一种是只有一个expression参数;另一种则有两个函数,除了expression参数外还包含一个root参数。像我们经常见到的那样,带一个参数的evalXXX函数会在设置一个默认值后调用带有两个参数的函数,我们看一个具体的例子evalString:

public String evalString(String expression) {
    return evalString(document, expression);
  }

public String evalString(Object root, String expression) {
    String result = (String) evaluate(expression, root, XPathConstants.STRING);
    result = PropertyParser.parse(result, variables);
    return result;
  }
  //expression表达式XPath表达式。
  //root起始上下文(例如一个节点)。
  //returnType所需的返回类型。
  private Object evaluate(String expression, Object root, QName returnType) {
    try {
    //在指定的上下文中解析一个XPath表达式,并返回结果作为指定的类型
      return xpath.evaluate(expression, root, returnType);
    } catch (Exception e) {
      throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
    }
  }
package org.apache.ibatis.parsing;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.ibatis.builder.BuilderException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * @author Clinton Begin
 */
//Mybatis 提供的XPathParser 类封装了XPath Document EntityResolver
  //XPathParser作用加载本地文档并解析文档的属性到Properties中
public class XPathParser {
//创建过程复杂,可不可以考虑builder模式
  private final Document document;//Document对象
  private boolean validation;//是否开启验证
  private EntityResolver entityResolver;//加载本地DTD文件
  private Properties variables;//mybatis-config.xml 中的 properties 标签下的键值对集合
  private XPath xpath;//XML 查询对象

  public XPathParser(String xml) {
    commonConstructor(false, null, null);
    this.document = createDocument(new InputSource(new StringReader(xml)));
  }

  public XPathParser(Reader reader) {
    commonConstructor(false, null, null);
    this.document = createDocument(new InputSource(reader));
  }

  public XPathParser(InputStream inputStream) {
    commonConstructor(false, null, null);
    this.document = createDocument(new InputSource(inputStream));
  }

  public XPathParser(Document document) {
    commonConstructor(false, null, null);
    this.document = document;
  }

  public XPathParser(String xml, boolean validation) {
    commonConstructor(validation, null, null);
    this.document = createDocument(new InputSource(new StringReader(xml)));
  }

  public XPathParser(Reader reader, boolean validation) {
    commonConstructor(validation, null, null);
    this.document = createDocument(new InputSource(reader));
  }

  public XPathParser(InputStream inputStream, boolean validation) {
    commonConstructor(validation, null, null);
    this.document = createDocument(new InputSource(inputStream));
  }

  public XPathParser(Document document, boolean validation) {
    commonConstructor(validation, null, null);
    this.document = document;
  }

  public XPathParser(String xml, boolean validation, Properties variables) {
    commonConstructor(validation, variables, null);
    this.document = createDocument(new InputSource(new StringReader(xml)));
  }

  public XPathParser(Reader reader, boolean validation, Properties variables) {
    commonConstructor(validation, variables, null);
    this.document = createDocument(new InputSource(reader));
  }

  public XPathParser(InputStream inputStream, boolean validation, Properties variables) {
    commonConstructor(validation, variables, null);
    this.document = createDocument(new InputSource(inputStream));
  }

  public XPathParser(Document document, boolean validation, Properties variables) {
    commonConstructor(validation, variables, null);
    this.document = document;
  }

  public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(new StringReader(xml)));
  }

  public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(reader));
  }

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

  public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = document;
  }

  public void setVariables(Properties variables) {
    this.variables = variables;
  }

  public String evalString(String expression) {
    return evalString(document, expression);
  }

  public String evalString(Object root, String expression) {
    String result = (String) evaluate(expression, root, XPathConstants.STRING);
    result = PropertyParser.parse(result, variables);
    return result;
  }

  public Boolean evalBoolean(String expression) {
    return evalBoolean(document, expression);
  }

  public Boolean evalBoolean(Object root, String expression) {
    return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN);
  }

  public Short evalShort(String expression) {
    return evalShort(document, expression);
  }

  public Short evalShort(Object root, String expression) {
    return Short.valueOf(evalString(root, expression));
  }

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

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

  public Long evalLong(String expression) {
    return evalLong(document, expression);
  }

  public Long evalLong(Object root, String expression) {
    return Long.valueOf(evalString(root, expression));
  }

  public Float evalFloat(String expression) {
    return evalFloat(document, expression);
  }

  public Float evalFloat(Object root, String expression) {
    return Float.valueOf(evalString(root, expression));
  }

  public Double evalDouble(String expression) {
    return evalDouble(document, expression);
  }

  public Double evalDouble(Object root, String expression) {
    return (Double) evaluate(expression, root, XPathConstants.NUMBER);
  }

  public List<XNode> evalNodes(String expression) {
    return evalNodes(document, expression);
  }

  public List<XNode> evalNodes(Object root, String expression) {
    List<XNode> xnodes = new ArrayList<XNode>();
    NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
    for (int i = 0; i < nodes.getLength(); i++) {
      xnodes.add(new XNode(this, nodes.item(i), variables));
    }
    return xnodes;
  }

  public XNode evalNode(String expression) {
    return evalNode(document, expression);
  }

  public XNode evalNode(Object root, String expression) {
    Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
    if (node == null) {
      return null;
    }
    return new XNode(this, node, variables);
  }

  private Object evaluate(String expression, Object root, QName returnType) {
    try {
      return xpath.evaluate(expression, root, returnType);
    } catch (Exception e) {
      throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
    }
  }

  private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    // 重要:这只能在公共构造函数后调用
    try {
      //1.文档创建者工厂
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);
      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);
      //2.文档创建者
      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 {
        }
      });
      //3.创建doc文档
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }
   //为什么使用构造器?????,只是使用了一个很简单的方法
  private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    //利用XPathFactory创建一个新的xpath对象
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }

}

5.XNode

接下来我们来了解parsing包中的最后一个类XNode。该类是对org.w3c.dom.Node类的一个封装,在Node类的基础上添加了一些新功能。

  private final Node node;//org.w3c.dom.Node对象
  private final String name;//Node节点名称
  private final String body;//节点的内容????
  private final Properties attributes;//节点属性集合
  private final Properties variables;//<property> </property> 节点下定义的键值对
  private final XPathParser xpathParser;//解析器

  //构造函数
  public XNode(XPathParser xpathParser, Node node, Properties variables) {
    this.xpathParser = xpathParser;
    this.node = node;
    this.name = node.getNodeName();
    this.variables = variables;
    this.attributes = parseAttributes(node);//获取当前节点的所有属性
    //获取当前节点的文本节点内容,当然获取到的数据是已经经过TokenHandler处理过的
    this.body = parseBody(node);
  }
  //获取当前节点的所有属性
  //属性节点 <span color="red" size="16" font="12">text</span>
  // color,size,font 属性
  private Properties parseAttributes(Node n) {
   Properties attributes = new Properties();
   NamedNodeMap attributeNodes = n.getAttributes();//获取所有属性节点
    if (attributeNodes != null) {
      for (int i = 0; i < attributeNodes.getLength(); i++) {
        Node attribute = attributeNodes.item(i);
        String value = PropertyParser.parse(attribute.getNodeValue(), variables);
        attributes.put(attribute.getNodeName(), value);
      }
    }
    return attributes;
  }
// 解析文本节点的内容
  private String parseBody(Node node) {
    String data = getBodyData(node);
    if (data == null) {
      NodeList children = node.getChildNodes();
      for (int i = 0; i < children.getLength(); i++) {
        Node child = children.item(i);
        data = getBodyData(child);
        if (data != null) {
          break;
        }
      }
    }
    return data;
  }
    private String getBodyData(Node child) {
    if (child.getNodeType() == Node.CDATA_SECTION_NODE
        || child.getNodeType() == Node.TEXT_NODE) {
      //判断子节点是否为文本节点或者文本节点的子节点
      String data = ((CharacterData) child).getData();
      data = PropertyParser.parse(data, variables);
      return data;
    }
    return null;
  }
package org.apache.ibatis.parsing;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * @author Clinton Begin
 */
public class XNode {

  private final Node node;//org.w3c.dom.Node对象
  private final String name;//Node节点名称
  private final String body;//节点的内容????
  private final Properties attributes;//节点属性集合
  private final Properties variables;//<property> </property> 节点下定义的键值对
  private final XPathParser xpathParser;//解析器

  //构造函数
  public XNode(XPathParser xpathParser, Node node, Properties variables) {
    this.xpathParser = xpathParser;
    this.node = node;
    this.name = node.getNodeName();
    this.variables = variables;
    this.attributes = parseAttributes(node);
    this.body = parseBody(node);
  }
  //将传入的Node节点包装为XNode类型
  public XNode newXNode(Node node) {
    return new XNode(xpathParser, node, variables);
  }

  //获取Node节点的父节点
  public XNode getParent() {
    //调用node节点原生方法获取一个Node类型的节点
    Node parent = node.getParentNode();
    if (parent == null || !(parent instanceof Element)) {
      //父节点为null或者节点的类型为不为元素节点
      return null;
    } else {
      //获取XNode的父亲(双亲),还是使用原始的node的操作,之后转化成XNode
      return new XNode(xpathParser, parent, variables);
    }
  }
  //获取Node在XML中的完整路径
  public String getPath() {
    StringBuilder builder = new StringBuilder();
    Node current = node;
    //递归调用,不为空且为元素节点
    while (current != null && current instanceof Element) {
      if (current != node) {
        builder.insert(0, "/");
      }
      builder.insert(0, current.getNodeName());
      current = current.getParentNode();
    }
    return builder.toString();
  }
  //?????不知道干什么用
  public String getValueBasedIdentifier() {
    StringBuilder builder = new StringBuilder();
    XNode current = this;
    while (current != null) {
      if (current != this) {
        builder.insert(0, "_");
      }
      String value = current.getStringAttribute("id",
              current.getStringAttribute("value",
              current.getStringAttribute("property", null)));
      if (value != null) {
        // 注意StringBuilder使用的插入模式,好像这个是的动态的字符串,,可以随便的插入和删除操作,很方便
        value = value.replace('.', '_');
        builder.insert(0, "]");
        builder.insert(0,
            value);
        builder.insert(0, "[");
      }
      builder.insert(0, current.getName());
      current = current.getParent();
    }
    return builder.toString();
  }
  /**
   * 下面是引用XPathParse的一些解析方法
   *
   * @param expression 解析的表达式
   * @return 解析之后的值
   */
  public String evalString(String expression) {
    return xpathParser.evalString(node, expression);
  }

  public Boolean evalBoolean(String expression) {
    return xpathParser.evalBoolean(node, expression);
  }

  public Double evalDouble(String expression) {
    return xpathParser.evalDouble(node, expression);
  }

  public List<XNode> evalNodes(String expression) {
    return xpathParser.evalNodes(node, expression);
  }

  public XNode evalNode(String expression) {
    return xpathParser.evalNode(node, expression);
  }

  public Node getNode() {
    return node;
  }

  public String getName() {
    return name;
  }
  /**
   * 下面都是应用getStringAttribute的方法获取的值
   */
  public String getStringBody() {
    return getStringBody(null);
  }

  public String getStringBody(String def) {
    if (body == null) {
      return def;
    } else {
      return body;
    }
  }

  public Boolean getBooleanBody() {
    return getBooleanBody(null);
  }

  public Boolean getBooleanBody(Boolean def) {
    if (body == null) {
      return def;
    } else {
      return Boolean.valueOf(body);
    }
  }

  public Integer getIntBody() {
    return getIntBody(null);
  }

  public Integer getIntBody(Integer def) {
    if (body == null) {
      return def;
    } else {
      return Integer.parseInt(body);
    }
  }

  public Long getLongBody() {
    return getLongBody(null);
  }

  public Long getLongBody(Long def) {
    if (body == null) {
      return def;
    } else {
      return Long.parseLong(body);
    }
  }

  public Double getDoubleBody() {
    return getDoubleBody(null);
  }

  public Double getDoubleBody(Double def) {
    if (body == null) {
      return def;
    } else {
      return Double.parseDouble(body);
    }
  }

  public Float getFloatBody() {
    return getFloatBody(null);
  }

  public Float getFloatBody(Float def) {
    if (body == null) {
      return def;
    } else {
      return Float.parseFloat(body);
    }
  }

  public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name) {
    return getEnumAttribute(enumType, name, null);
  }

  public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name, T def) {
    String value = getStringAttribute(name);
    if (value == null) {
      return def;
    } else {
      return Enum.valueOf(enumType, value);
    }
  }

  public String getStringAttribute(String name) {
    return getStringAttribute(name, null);
  }

  public String getStringAttribute(String name, String def) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return def;
    } else {
      return value;
    }
  }

  public Boolean getBooleanAttribute(String name) {
    return getBooleanAttribute(name, null);
  }

  public Boolean getBooleanAttribute(String name, Boolean def) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return def;
    } else {
      return Boolean.valueOf(value);
    }
  }

  public Integer getIntAttribute(String name) {
    return getIntAttribute(name, null);
  }

  public Integer getIntAttribute(String name, Integer def) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return def;
    } else {
      return Integer.parseInt(value);
    }
  }

  public Long getLongAttribute(String name) {
    return getLongAttribute(name, null);
  }

  public Long getLongAttribute(String name, Long def) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return def;
    } else {
      return Long.parseLong(value);
    }
  }

  public Double getDoubleAttribute(String name) {
    return getDoubleAttribute(name, null);
  }

  public Double getDoubleAttribute(String name, Double def) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return def;
    } else {
      return Double.parseDouble(value);
    }
  }

  public Float getFloatAttribute(String name) {
    return getFloatAttribute(name, null);
  }

  public Float getFloatAttribute(String name, Float def) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return def;
    } else {
      return Float.parseFloat(value);
    }
  }

  //获取孩子节点的List<XNode>集合
  public List<XNode> getChildren() {
    List<XNode> children = new ArrayList<XNode>();
    NodeList nodeList = node.getChildNodes();
    if (nodeList != null) {
      for (int i = 0, n = nodeList.getLength(); i < n; i++) {
        Node node = nodeList.item(i);
        if (node.getNodeType() == Node.ELEMENT_NODE) {
          //包装为XNode 节点
          children.add(new XNode(xpathParser, node, variables));
        }
      }
    }
    return children;
  }

  //获取当前节点的所有孩子节点的属性值name/value
  public Properties getChildrenAsProperties() {
    Properties properties = new Properties();
    for (XNode child : getChildren()) {
      String name = child.getStringAttribute("name");
      String value = child.getStringAttribute("value");
      if (name != null && value != null) {
        properties.setProperty(name, value);
      }
    }
    return properties;
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append("<");
    builder.append(name);
    for (Map.Entry<Object, Object> entry : attributes.entrySet()) {
      builder.append(" ");
      builder.append(entry.getKey());
      builder.append("=\"");
      builder.append(entry.getValue());
      builder.append("\"");
    }
    List<XNode> children = getChildren();
    if (!children.isEmpty()) {
      builder.append(">\n");
      for (XNode node : children) {
        builder.append(node.toString());
      }
      builder.append("</");
      builder.append(name);
      builder.append(">");
    } else if (body != null) {
      builder.append(">");
      builder.append(body);
      builder.append("</");
      builder.append(name);
      builder.append(">");
    } else {
      builder.append("/>");
    }
    builder.append("\n");
    return builder.toString();
  }

  private Properties parseAttributes(Node n) {
    Properties attributes = new Properties();
    NamedNodeMap attributeNodes = n.getAttributes();//获取所有属性节点
    if (attributeNodes != null) {
      for (int i = 0; i < attributeNodes.getLength(); i++) {
        Node attribute = attributeNodes.item(i);
        String value = PropertyParser.parse(attribute.getNodeValue(), variables);
        attributes.put(attribute.getNodeName(), value);
      }
    }
    return attributes;
  }

  private String parseBody(Node node) {
    String data = getBodyData(node);
    if (data == null) {
      NodeList children = node.getChildNodes();
      for (int i = 0; i < children.getLength(); i++) {
        Node child = children.item(i);
        data = getBodyData(child);
        if (data != null) {
          break;
        }
      }
    }
    return data;
  }

  private String getBodyData(Node child) {
    if (child.getNodeType() == Node.CDATA_SECTION_NODE
        || child.getNodeType() == Node.TEXT_NODE) {
      //判断子节点是否为文本节点或者文本节点的子节点
      String data = ((CharacterData) child).getData();
      data = PropertyParser.parse(data, variables);
      return data;
    }
    return null;
  }

}

二.最佳实战
我们写一个示例看看这个包中的类是如何运行的。从上面的类和接口的介绍中可以发现,XPathParser类是比较上层的类(这里的上层,不是说这个类是各个类的超类,而是说它依赖的类较多)。用XPathParser类解析一段数据源定义的配置文件片段,首先设定properties文件的内容,文件名为jdbc.properties:

package org.apache.ibatis.parsing;

import org.apache.ibatis.io.Resources;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class Main {

        public static void main(String[] args) throws FileNotFoundException, IOException {
            Properties properties = new Properties();
            //加载参数: properties.load();
            properties.load(new FileInputStream("src/main/java/org/apache/ibatis/parsing/config/config.xml"));
            String xml ="<?xml version='1.0' encoding='utf-8'?>"+
                    "<bean id='dataSource' class='org.apache.commons.dbcp.BasicDataSource' destroy-method='close' >    " +
                    "    <property name='driverClassName' value='${driver}' />" +
                    "    <property name='url' value='${url}' />    " +
                    "    <property name='username' value='${username}' />    " +
                    "    <property name='password' value='${password}' />    " +
                    "</bean>";
            //初始化XPathParser
            XPathParser xPathParser = new XPathParser(xml,false,properties);
            //解析表达式,获取XNode对象
            XNode xnode = xPathParser.evalNode("//bean");
            //下面调用对
            System.out.println(xnode.getName());
            System.out.println(xnode.getStringAttribute("id"));
            System.out.println(xnode.getStringAttribute("class"));
            System.out.println(xnode.getPath());
        }

}

jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=1q2w3e

输出结果

bean
dataSource
org.apache.commons.dbcp.BasicDataSource
bean
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值