mybatis源碼解析三之(xml解析器dom和sax)5

xpath解析xml

public class XPathParser {

  private final Document document;
  //是否開啓驗證
  private boolean validation;
  //加載本地DTD
  private EntityResolver entityResolver;
  //Properties標籤定義值
  private Properties variables;
  private XPath xpath;
}

解析DTD

spring專欄有自定義命名空間解析,就不闡述了

public class XMLMapperEntityResolver implements EntityResolver {

  //指定mybatis-config.xml的DTD
  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";

  //指定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);
        //查找sysId指定的DTD文檔,並讀取
        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());
    }
  }
  }

構造Xpath和document

  public XPathParser(String xml) {
      //構造時,初始化xpath
    commonConstructor(false, null, null);
    this.document = createDocument(new InputSource(new StringReader(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 {

      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      factory.setValidating(validation);

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

      DocumentBuilder builder = factory.newDocumentBuilder();
      //設置DTD解析器
      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 {
          // NOP
        }
      });
      //加載XML
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

解析String

  public String evalString(Object root, String expression) {
    String result = (String) evaluate(expression, root, XPathConstants.STRING);
    //設置節點默認值
    result = PropertyParser.parse(result, variables);
    return result;
  }
  public static String parse(String string, Properties variables) {
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    //指定處理的佔位符格式爲${}
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    return parser.parse(string);
  }

public class GenericTokenParser {

  //佔位符開始標記
  private final String openToken;
  //佔位符結束標記
  private final String closeToken;
  //解析佔位符
  private final TokenHandler handler;
  
  /**
   * 將text根據前後佔位符解析
   * @param text
   * @return
   */
  public String parse(String text) {
    if (text == null || text.isEmpty()) {
      return "";
    }
    // search open token
    //查找第一個開啓佔位符位置
    int start = text.indexOf(openToken);
    if (start == -1) {
      return text;
    }
    char[] src = text.toCharArray();
    int offset = 0;
    //線程安全,記錄佔位符的值
    final StringBuilder builder = new StringBuilder();
    StringBuilder expression = null;
    while (start > -1) {
      if (start > 0 && src[start - 1] == '\\') {
        // this open token is escaped. remove the backslash and continue.
        //遇到轉義的開始標記,直接將前面的字符串以及開始標記追加
        builder.append(src, offset, start - offset - 1).append(openToken);
        offset = start + openToken.length();
      } else {
        // found open token. let's search close token.
        //查找到開始標記,且未轉義
        if (expression == null) {
          expression = new StringBuilder();
        } else {
          expression.setLength(0);
        }
        //前面字符追加
        builder.append(src, offset, start - offset);
        offset = start + openToken.length();
        //從offset後繼續查找結束標記
        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);
            break;
          }
        }
        if (end == -1) {
          //未找到結束標記
          // close token was not found.
          builder.append(src, start, src.length - start);
          offset = src.length;
        } else {
          //將佔位符的值交給handler處理,並保存builder
          builder.append(handler.handleToken(expression.toString()));
          offset = end + closeToken.length();
        }
      }
      //移動start
      start = text.indexOf(openToken, offset);
    }
    if (offset < src.length) {
      builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
  }
}
  private static class VariableTokenHandler implements TokenHandler {
    //Properties標籤定義值,用於替換佔位符
    private final Properties variables;
    //是否支持佔位符中使用默認的功能
    private final boolean enableDefaultValue;
    //指定佔位符和默認值之間的分隔符
    private final String defaultValueSeparator;
    
  @Override
    public String handleToken(String content) {
      //variables非空
      if (variables != null) {
        String key = content;
        //是否支持佔位符中使用默認值功能
        if (enableDefaultValue) {
          //查找分割符
          final int separatorIndex = content.indexOf(defaultValueSeparator);
          String defaultValue = null;
          if (separatorIndex >= 0) {
            //獲取佔位符名稱
            key = content.substring(0, separatorIndex);
            //獲取默認值
            defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
          }
          if (defaultValue != null) {
            //查找指定佔位符
            return variables.getProperty(key, defaultValue);
          }
        }
        //不支持默認值,則直接找
        if (variables.containsKey(key)) {
          return variables.getProperty(key);
        }
      }
      //variables空
      return "${" + content + "}";
    }
}

解析XNode

  public List<XNode> evalNodes(Object root, String expression) {
    //XNode,mybatis實現,封裝node
    List<XNode> xnodes = new ArrayList<>();
    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 class XNode {

  //w3c對象
  private final Node node;
  //節點名
  private final String name;
  //節點內容
  private final String body;
  //節點屬性集合
  private final Properties attributes;
  //Properties對應配置文件
  private final Properties variables;
  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);
  }

  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);
        //使用PropertyParser 處理每個屬性中的佔位符
        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();
      //使用PropertyParser 處理文本節點中的佔位符
      data = PropertyParser.parse(data, variables);
      return data;
    }
    return null;
  }
}

對反射支持

public class Reflector {
  //對應的Class 類型
  private final Class<?> type;
  //可讀屬性的名稱集合,可讀屬性就是存在相應getter 方法的屬性,初始值爲空數紐
  private final String[] readablePropertyNames;
  //可寫屬性的名稱集合,可寫屬性就是存在相應setter 方法的屬性,初始值爲空數紐
  private final String[] writablePropertyNames;
  //記錄了屬性相應的setter 方法, key 是屬性名稱, value 是Invoker 對象
  private final Map<String, Invoker> setMethods = new HashMap<>();
  //屬性相應的getter 方法集合, key 是屬性名稱, value 也是Inv o ker 對象
  private final Map<String, Invoker> getMethods = new HashMap<>();
  //記錄了屬性相應的setter 方法的參數值類型, ke y 是屬性名稱, value 是setter 方法的參數類型
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  //記錄了屬性相應的getter 方法的返回位類型, key 是屬性名稱, value 是getter 方法的返回位類型
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  //記錄了默認構造方法
  private Constructor<?> defaultConstructor;
  //記錄了所有屬性名稱的集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

  public Reflector(Class<?> clazz) {
    type = clazz;
    //查找clazz 的默認構造方法(無參構造方法)
    addDefaultConstructor(clazz);
    //處理clazz 中的getter 方法,填充getMethods 集合和getTypes 集合
    addGetMethods(clazz);
    //處理clazz 中的set ter 方法,填充setMethods 集合和set Types 集合
    addSetMethods(clazz);
    //處理沒有g etter I setter 方法的字段
    addFields(clazz);
    //根據getMethodslsetMethods 集合,初始化可讀/寫屬性的名稱集合
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    //初始化caseinsensitivePropertyMap 集合,其中記錄了所有大寫格式的屬性名稱
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

   private void addGetMethods(Class<?> clazz) {
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    //獲取指定類以及其父類和接口中定義的方法
    Method[] methods = getClassMethods(clazz);
    //對get方法過濾
    Arrays.stream(methods).filter(m ->
        //JavaBean 中getter 方法的方法名長度大於3 且必須以” get ” 開頭
      m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
      .forEach(m ->
          //記錄到conflictingGetters集合中
        addMethodConflict(conflictingGetters,
          //獲取對應的屬性名稱
          PropertyNamer.methodToProperty(m.getName()), m));
    //對方法同名返回值不一樣的處理
    resolveGetterConflicts(conflictingGetters);
  }

 private Method[] getClassMethods(Class<?> clazz) {
    //用於記錄指定類中定義的全部方法的唯一簽名以及對應的Meth od 對象
    Map<String, Method> uniqueMethods = new HashMap<>();
    Class<?> currentClass = clazz;
    while (currentClass != null && currentClass != Object.class) {
      //記錄currentClass 這個類中定義的全部方法.橋方法除外
      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());

      // we also need to look for interface methods -
      // because the class may be abstract
      //記錄接口中定義的方法
      Class<?>[] interfaces = currentClass.getInterfaces();
      for (Class<?> anInterface : interfaces) {
        addUniqueMethods(uniqueMethods, anInterface.getMethods());
      }
      //獲取父類,繼續while 循環
      currentClass = currentClass.getSuperclass();
    }

    Collection<Method> methods = uniqueMethods.values();

    return methods.toArray(new Method[0]);
  }


  private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
      //過濾橋接方法,也就是爲了兼容泛型,重新定義了一個方法,調用原來的方法,定位橋
      if (!currentMethod.isBridge()) {
      //方法得到的方法簽名是全局唯一的,可以作爲該方法 的唯一標識
        String signature = getSignature(currentMethod);
        // check to see if the method is already known
        // if it is known, then an extended class must have
        // overridden a method
        //檢測是否在子類中已經添加過該方法,如果在子類中已經添加過,無須再向uniqueMethods 集合中添加該方法了
        if (!uniqueMethods.containsKey(signature)) {
        //記錄該簽名和方法的對應關係
          uniqueMethods.put(signature, currentMethod);
        }
      }
    }
  }
}

 private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
      Method winner = null;
      String propName = entry.getKey();
      boolean isAmbiguous = false;
      for (Method candidate : entry.getValue()) {
        if (winner == null) {
          winner = candidate;
          continue;
        }
        //同一屬性名稱存在多個getter方法,則需要比較這些getter方法的返回,選擇getter方法
        //迭代過程中的臨時變量,用於記錄迭代到目前爲止,最適合作爲getter 方法的Method
        Class<?> winnerType = winner.getReturnType();
        Class<?> candidateType = candidate.getReturnType();
        if (candidateType.equals(winnerType)) {
          //對boolean處理
          if (!boolean.class.equals(candidateType)) {
            isAmbiguous = true;
            break;
          } else if (candidate.getName().startsWith("is")) {
            winner = candidate;
          }
        } else if (candidateType.isAssignableFrom(winnerType)) {
          // OK getter type is descendant
          //當前最適合的方法的返回佳是當前方法返回的子類,什麼都不做,當前最適合的方法 依然不變
        } else if (winnerType.isAssignableFrom(candidateType)) {
          //當前方法的返回位是當前最適合的方法的返回值的子類,更新臨時交量getter,當前的getter 方法成爲最適合的getter 方法
          winner = candidate;
        } else {
          //返回值相同,二義性,拋出異常
          isAmbiguous = true;
          break;
        }
      }
      //添加到getMethods 集合並填充get Types 集合
      addGetMethod(propName, winner, isAmbiguous);
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章