Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源碼分析

前言

從2019年7月4日開始,每天盡量抽一個小時出來,到了2019年10月9日總算把Mybatis3.5.1的源碼給讀完了,雖然是很累,但確實收獲不少,至少比起看教程,收獲更多。
因為我是直接把說明寫到代碼上的,所以我不會再多說明某個類,某個方法。所以本博客只適合那些正在看Mybatis源碼的讀者,幫助他們更好的理解

Mybatis3.5.1源碼分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源碼解析
  2. Mybatis-Configuration源碼解析
  3. Mybatis-事務(wù)對象源碼解析
  4. Mybatis-數(shù)據(jù)源源碼解析
  5. Mybatis緩存策略源碼解析
  6. Mybatis-DatabaseIdProvider源碼解析
  7. Mybatis-TypeHandler源碼解析
  8. Mybatis-Reflector源碼解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源碼分析
  10. Mybatis-Mapper各類標簽封裝類源碼解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源碼分析
  12. Mybatis-MapperAnnotationBuilder源碼分析
  13. [Mybatis-MetaObject,MetaClass源碼解析]http://www.itdecent.cn/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源碼解析
  15. Mybatis-SqlSource源碼解析
  16. Mybatis-SqlNode源碼解析
  17. Mybatis-KeyGenerator源碼解析
  18. Mybatis-Executor源碼解析
  19. Mybatis-ParameterHandler源碼解析
  20. Mybatis-StatementHandler源碼解析
  21. Mybatis-DefaultResultSetHandler(一)源碼解析
  22. Mybatis-DefaultResultSetHandler(二)源碼解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源碼分析
  24. Mybatis-MapperProxy源碼解析
  25. Mybatis-SqlSession源碼解析
  26. Mybatis-Interceptor源碼解析

SqlSessionFactoryBuilder

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.session;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

/**
 * Builds {@link SqlSession} instances.
 * 利用XML或者Java編碼獲得資源來構(gòu)建SqlSessionFactory
 * @author Clinton Begin
 */
public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }

  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  /**
   *
   * @param inputStream mybatis-config.xml配置文件流
   */
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }

  /**
   * 新建一個對mybatis的XML配置文件進行解析的解析器對應(yīng)配置文件先進性解析封裝
   * @param inputStream 配置文件文件流
   * @param environment 加載哪種環(huán)境(開發(fā)環(huán)境/生產(chǎn)環(huán)境),包括數(shù)據(jù)源和事務(wù)管理器
   * @param properties 屬性配置文件,那些屬性可以用${propName}語法形式多次用在配置文件中
   * @return
   */
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //新建一個對mybatis的XML配置文件進行解析的解析器
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //parse:對Mybatis配置文件xml中的標簽信息進行解析封裝,然后添加到Mybatis全局配置信息中,返回Mybatis全局配置信息對象
      return build(parser.parse());
    } catch (Exception e) {
      //包裝解析異常,進行更加具體的描述
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      //重置異常上下文實例
      ErrorContext.instance().reset();
      try {
        //關(guān)閉配置文件文件流
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}

BaseBuilder

/**
 * 主要提供{@link #configuration},{@link #typeHandlerRegistry},{@link #typeHandlerRegistry}的處理。
 * @author Clinton Begin
 */
public abstract class BaseBuilder {
  /**
   * Mybatis的全局配置信息
   */
  protected final Configuration configuration;
  /**
   * 類型別名注冊器
   */
  protected final TypeAliasRegistry typeAliasRegistry;
  /**
   * 類型處理器的注冊器
   */
  protected final TypeHandlerRegistry typeHandlerRegistry;


  /**
   * 從{@link @configuration}中獲取類型別名注冊器和類型處理器的注冊器賦值
   * 給{@link #typeAliasRegistry}和{@link #typeHandlerRegistry}
   * @param configuration mybatsi全局配置信息
   */
  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }

  /**
   * 獲取 Mybatis的配置信息 {@link #configuration}
   * @return
   */
  public Configuration getConfiguration() {
    return configuration;
  }

  /**
   * 如果{@link @regex}為null,就用{@link @defaultValue}
   */
  protected Pattern parseExpression(String regex, String defaultValue) {
    return Pattern.compile(regex == null ? defaultValue : regex);
  }

  /**
   * 如果{@link @value}為null,就返回{@link @defaultVale}
   */
  protected Boolean booleanValueOf(String value, Boolean defaultValue) {
    return value == null ? defaultValue : Boolean.valueOf(value);
  }

  /**
   * 如果{@link @value}為null,就返回{@link @defaultVale}
   */
  protected Integer integerValueOf(String value, Integer defaultValue) {
    return value == null ? defaultValue : Integer.valueOf(value);
  }

  /**
   * 將{@link @value}以','分隔的形式轉(zhuǎn)換成{@link Set},如果{@link @value}為null,就用{@link @defaultValue}
   */
  protected Set<String> stringSetValueOf(String value, String defaultValue) {
    value = value == null ? defaultValue : value;
    return new HashSet<>(Arrays.asList(value.split(",")));
  }

  /**
   * 找出對應(yīng){@link @alias}的{@link JdbcType},若{@link @alias}為null返回null,若沒有找到就會拋出{@link BuilderException}
   */
  protected JdbcType resolveJdbcType(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return JdbcType.valueOf(alias);
    } catch (IllegalArgumentException e) {
      throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
    }
  }

  /**
   * 找出對應(yīng){@link @alias}的{@link ResultSetType},若{@link @alias}為null返回null,若沒有找到就會拋出{@link BuilderException}
   */
  protected ResultSetType resolveResultSetType(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return ResultSetType.valueOf(alias);
    } catch (IllegalArgumentException e) {
      throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);
    }
  }

  /**
   * 找出對應(yīng){@link @alias}的{@link ParameterMode},若{@link @alias}為null返回null,若沒有找到就會拋出{@link BuilderException},
   * <p>ParameterMode: {@link ParameterMode#IN},{@link ParameterMode#OUT},{@link ParameterMode#INOUT}</p>
   */
  protected ParameterMode resolveParameterMode(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return ParameterMode.valueOf(alias);
    } catch (IllegalArgumentException e) {
      throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);
    }
  }

  /**
   * 創(chuàng)建{@link @alias}的實例,{@link @alias}為null返回null,若在創(chuàng)建拋出任何異常信息都會封裝到{@link BuilderException}拋出
   */
  protected Object createInstance(String alias) {
    Class<?> clazz = resolveClass(alias);
    if (clazz == null) {
      return null;
    }
    try {
      return resolveClass(alias).newInstance();
    } catch (Exception e) {
      throw new BuilderException("Error creating instance. Cause: " + e, e);
    }
  }

  /**
   * 通過類別名注冊器typeAliasRegistry解析出對應(yīng)的類 實際調(diào)用{@link #resolveAlias(String)},返回類實例
   */
  protected <T> Class<? extends T> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }

  /**
   * 構(gòu)建TypeHandler,不沒有注冊到{@link #typeHandlerRegistry},只是返回TypeHandler實例對象,實際調(diào)用{@link #resolveTypeHandler(Class, Class)}
   * @param javaType 可以不傳的,不傳就用typeHandlerAlias的無參構(gòu)造方法,傳就嘗試調(diào)用帶有接收javaType的構(gòu)造方法[p:即使調(diào)用失敗也不會拋出異常,而是調(diào)用無參構(gòu)造方法]
   * @param typeHandlerAlias 若null,該方法直接返回null;若傳入的包+類沒有繼承{@link TypeHandler}會拋出{@link BuilderException}
   */
  protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) {
    if (typeHandlerAlias == null) {
      return null;
    }
    //獲取typeHandlerAlias對應(yīng)的類
    Class<?> type = resolveClass(typeHandlerAlias);
    if (type != null && !TypeHandler.class.isAssignableFrom(type)) {
      throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface");
    }
    @SuppressWarnings("unchecked") // already verified it is a TypeHandler 已經(jīng)嚴重type是TypeHandler類。
    Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type;
    return resolveTypeHandler(javaType, typeHandlerType);
  }

  /**
   * 構(gòu)建TypeHandler,沒有注冊到{@link #typeHandlerRegistry},只是返回TypeHandler實例對象
   * @param javaType 可以不傳的,但是不傳,也是可以的,但是不建議。
   * @param typeHandlerType 若為null,該方法直接返回null
   * @return
   */
  protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
    if (typeHandlerType == null) {
      return null;
    }
    // javaType ignored for injected handlers see issue #746 for full detail
    //先從緩存Map中獲取
    TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
    if (handler == null) {
      // not in registry, create a new one
      //實例化一個新的typeHandler對象
      handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
    }
    return handler;
  }

  /**
   * 通過類別名注冊器typeAliasRegistry找出對應(yīng)的類
   */
  protected <T> Class<? extends T> resolveAlias(String alias) {
    return typeAliasRegistry.resolveAlias(alias);
  }
}

XMLConfigBuilder

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.builder.xml;

import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import javax.sql.DataSource;

import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.loader.ProxyFactory;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.AutoMappingBehavior;
import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.LocalCacheScope;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.JdbcType;

/**
 * 對mybatis的XML配置文件進行解析的類
 * https://blog.csdn.net/fageweiketang/article/details/80794847
 * @author Clinton Begin
 * @author Kazuki Shimizu
 */
public class XMLConfigBuilder extends BaseBuilder {

  private boolean parsed;
  private final XPathParser parser;
  /**
   * Mybatis運行時所用的數(shù)據(jù)庫環(huán)境ID,一般從 &lt;environments/&gt; 的 default 屬性中獲取
   */
  private String environment;
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();

  public XMLConfigBuilder(Reader reader) {
    this(reader, null, null);
  }

  public XMLConfigBuilder(Reader reader, String environment) {
    this(reader, environment, null);
  }

  public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  public XMLConfigBuilder(InputStream inputStream) {
    this(inputStream, null, null);
  }

  public XMLConfigBuilder(InputStream inputStream, String environment) {
    this(inputStream, environment, null);
  }

  /**
   *
   * @param inputStream 配置文件InputStream
   * @param environment 加載哪種環(huán)境(開發(fā)環(huán)境/生產(chǎn)環(huán)境),包括數(shù)據(jù)源和事務(wù)管理器
   * @param props 屬性配置文件,那些屬性可以用${propName}語法形式多次用在配置文件中
   */
  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    //XPathParser:XML解析器
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  /**
   *
   * @param parser Mybatis配置文件xml對應(yīng)的XML解析器
   * @param environment 加載哪種環(huán)境(開發(fā)環(huán)境/生產(chǎn)環(huán)境),包括數(shù)據(jù)源和事務(wù)管理器
   * @param props 屬性配置文件,那些屬性可以用${propName}語法形式多次用在配置文件中
   */
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //新建一個Mybatis全局配置信息類對象
    super(new Configuration());
    //設(shè)置錯誤報文實例的資源引用
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);//設(shè)置屬性文件的變量到配置類configuration
    this.parsed = false;//parse表示是否已經(jīng)解析了
    this.environment = environment;
    this.parser = parser;//XML
  }

  /**
   * 對Mybatis配置文件xml中的標簽信息進行解析封裝,然后添加到Mybatis全局配置信息中
   * @return Mybatis全局配置信息對象
   */
  public Configuration parse() {
    if (parsed) {
      //每個XMLConfig Builder只能使用一次;
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //對應(yīng)<configuation>節(jié)點
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  /**
   * 解析mybatis-config.xml所有標簽信息,并實例化標簽對應(yīng)的對象,配置進 {@link XMLConfigBuilder#configuration} 里
   * @param root
   */
  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(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);
    }
  }

  /**
   * 讀取<setting></setting>便簽信息,每個<setting/>的name屬性對應(yīng)著Configuration的屬性變量名,
   * 該放會檢驗<setting/>標簽的name屬性能不能在Configuration查找出來,如果查找不出來,拋出異常
   */
  private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    for (Object key : props.keySet()) {
      if (!metaConfig.hasSetter(String.valueOf(key))) {//這里就是判斷有沒有對應(yīng)的屬性在configuration里,沒有就拋出異常
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

  /**
   * 加載虛擬文件系統(tǒng)配置,讀取服務(wù)器資源
   * VFS含義是虛擬文件系統(tǒng);主要是通過程序能夠方便讀取本地文件系統(tǒng)、FTP文件系統(tǒng)等系統(tǒng)中的文件資源
   * Mybatis中提供了VFS這個配置,主要是通過該配置可以加載自定義的虛擬文件系統(tǒng)應(yīng)用程序
   */
  private void loadCustomVfs(Properties props) throws ClassNotFoundException {
    String value = props.getProperty("vfsImpl");
    if (value != null) {
      String[] clazzes = value.split(",");
      for (String clazz : clazzes) {
        if (!clazz.isEmpty()) {
          @SuppressWarnings("unchecked")
          Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
          configuration.setVfsImpl(vfsImpl);
        }
      }
    }
  }

  /**
   * 制定日志的實現(xiàn)(log4j等)
   */
  private void loadCustomLogImpl(Properties props) {
    Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));//找出logImpl對應(yīng)的日志類
    configuration.setLogImpl(logImpl);
  }

  /**
   * 類型別名元素
   * @param parent <typeAliases/>
   */
  private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {//如果是包名
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);//掃描包下的所有符合的類
        } else {
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);//通過類加載器加載對應(yīng)的來
            //注冊別名到typeAliasRegistry
            if (alias == null) {
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

  /**
   * <plugins/>元素,每個插件就是mybatis的攔截器。
   * @param parent <plugins/>標簽
   */
  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();//加載對應(yīng)的攔截類,并new一個實例
        interceptorInstance.setProperties(properties);//加上<plugin/>標簽內(nèi)的<property>標簽信息
        configuration.addInterceptor(interceptorInstance);//注冊到configuration的攔截器鏈中
      }
    }
  }

  /**
   *  ObjectFactory 標簽 -- [對象工廠 {@link ObjectFactory}]
   * @param context
   * @throws Exception
   */
  private void objectFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties properties = context.getChildrenAsProperties();
      ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();//加載對應(yīng)的ObjectFactory類,并new一個實例
      factory.setProperties(properties);//加上<ObjectFactory/>標簽內(nèi)的<property>標簽信息
      configuration.setObjectFactory(factory);//注冊到objectFactory
    }
  }

  /**
   *  ObjectWrapperFactory 標簽 -- [對象包裝類工廠{@link ObjectWrapperFactory}]
   * @param context
   * @throws Exception
   */
  private void objectWrapperFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();//加載對應(yīng)的ObjectWrapperFactory類,并new一個實例
      configuration.setObjectWrapperFactory(factory);//注冊到objectWrapperFactory
    }
  }

  /**
   * reflectorFactory標簽 -- [反射信息類工廠{@link ReflectorFactory}]
   * @param context
   * @throws Exception
   */
  private void reflectorFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();//加載對應(yīng)的ReflectorFactory類,并new一個實例
      configuration.setReflectorFactory(factory);//注冊到ReflectorFactory
    }
  }

  /**
   * 讀取<Properties>標簽的屬性變量,以及合并configuration的屬性變量,然后重新賦值到parser和configuration的屬性變量中。
   */
  private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      if (resource != null && url != null) {//resource屬性和url屬性在<properties>標簽中只能指定一個
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      //加載對應(yīng)路徑的屬性
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //加載configuration的屬性
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);//設(shè)置解析器的屬性變量
      configuration.setVariables(defaults);//設(shè)置Configuration的屬性變量
    }
  }

  /**
   * 將<setting/>標簽的設(shè)置全部設(shè)置進去Configuation
   */
  private void settingsElement(Properties props) {
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
  }

  /**
   * &lt;Environments/&gt;標簽元素
   * <p>
   *     獲取&lt;Environments/&gt;下的default屬性取得當前Mybatis所需要的環(huán)境ID并賦值給 {@link XMLConfigBuilder#environment},
   *     然后獲取&lt;environment/&gt;的id屬性(環(huán)境ID)并賦值給{@link @id},判斷{@link @id}是不是{@link XMLConfigBuilder#environment},
   *     是就做以下操作
   *     <ol>
   *         <li>獲取環(huán)境ID</li>
   *         <li>獲取事務(wù)管理器工廠的實例對象,賦值給 {@link @txFactory}</li>
   *         <li>獲取數(shù)據(jù)庫數(shù)據(jù)源工廠的實例對象{@link @dsFactory},然后從{@link @dsFactory} 獲取數(shù)據(jù)源,賦值給 {@link @dataSoure}</li>
   *         <li>傳入{@link @txFactory},{@link @dataSoure},{@link @id} 構(gòu)建 {@link Environment} 傳入 {@link XMLConfigBuilder#configuration}</li>
   *     </ol>
   * </p>
   * @param context
   * @throws Exception
   */
  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");//默認環(huán)境ID,指<environment/>的ID屬性
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");//環(huán)境ID
        //只有符合指定的默認環(huán)境ID的環(huán)境ID才會被解析
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//事務(wù)管理器工廠
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//數(shù)據(jù)源工廠
          DataSource dataSource = dsFactory.getDataSource();//取出數(shù)據(jù)源
          //構(gòu)建環(huán)境
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

  /**
   * &lt;databaseIdProvider/&gt; 解析?!緮?shù)據(jù)庫廠商標識】
   * <p>
   *    MyBatis 可以根據(jù)不同的數(shù)據(jù)庫廠商執(zhí)行不同的語句,這種多廠商的支持是基于映射語句中的 databaseId 屬性。
   *    MyBatis 會加載不帶 databaseId 屬性和帶有匹配當前數(shù)據(jù)庫 databaseId 屬性的所有語句。
   *    如果同時找到帶有 databaseId 和不帶 databaseId 的相同語句,則后者會被舍棄。
   *    為支持多廠商特性設(shè)置 databaseIdProvider
   * </p>
   * <p>
   *     Mybatis內(nèi)置了一個 {@link org.apache.ibatis.mapping.VendorDatabaseIdProvider},對type屬性設(shè)置別名'DB_VENDOR'即可使用。
   * </p>
   * 參考文檔
   * <ol>
   *     <li>https://www.cnblogs.com/hellowhy/p/9676037.html</li>
   *     <li>http://www.mybatis.org/mybatis-3/zh/configuration.html#databaseIdProvider</li>
   * </ol>
   */
  private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
    if (context != null) {
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      //糟糕的補丁保持向后兼容性;可能由于之前的版本不是DB_VENDOR,所以加上這段代碼。
      if ("VENDOR".equals(type)) {
        type = "DB_VENDOR";//{@link VendorDatabaseIdProvider}的別名

      }
      Properties properties = context.getChildrenAsProperties();
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
      databaseIdProvider.setProperties(properties);
    }
    /**
     * 從environment中獲取數(shù)據(jù)源,交給databaseIdProvider獲取數(shù)據(jù)庫ID。
     * 數(shù)據(jù)ID,其實就是數(shù)據(jù)庫名,或者properties對應(yīng)key的value(這個需要用戶去配置)
     */
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      configuration.setDatabaseId(databaseId);
    }
  }

  /**
   * <environment/>下的<dataSource />標簽信息,返回事務(wù)工廠 {@link TransactionFactory}
   * <ul>
   *     <li> type= 'JDBC' ,返回 {@link org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory}</li>
   *     <li> type= 'MANAGED' ,返回 {@link org.apache.ibatis.transaction.managed.ManagedTransactionFactory}</li>
   *     <li> type='包+類名',返回對應(yīng)類實例 {@link TransactionFactory} ,該類必須保證繼承了 {@link TransactionFactory} 接口</li>
   * </ul>
   * @throws BuilderException 沒有設(shè)置<transactionManager />就會拋出異常
   */
  private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }

  /**
   * <environment/>下的<transactionManager />標簽信息,返回數(shù)據(jù)源工廠 {@link DataSourceFactory}
   * <ul>
   *     <li> type= 'JNDI' ,返回 {@link org.apache.ibatis.datasource.jndi.JndiDataSourceFactory}</li>
   *     <li> type= 'POOLED' ,返回 {@link org.apache.ibatis.datasource.pooled.PooledDataSourceFactory}</li>
   *     <li> type= 'UNPOOLED' ,返回 {@link org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory}</li>
   *     <li> type='包+類名',返回對應(yīng)類實例 {@link DataSourceFactory} ,該類必須保證繼承了 {@link DataSourceFactory} 接口</li>
   * </ul>
   * @throws BuilderException 沒有設(shè)置<transactionManager />就會拋出異常
   */
  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

  /**
   *&lt;typeHandler/&gt;解釋
   * <p>
   *     調(diào)用{#typeHandlerRegistry}注冊TypeHandler。就算javaType,jdbcType都是null,都能注冊得到TypeHandler,只不過javaType,jdbcType都是null
   *     。但是我不建議這樣做、
   * </p>
   * @param parent
   */
  private void typeHandlerElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //掃描包,找出尋找TypeHandler的類
          String typeHandlerPackage = child.getStringAttribute("name");
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            //
            if (jdbcType == null) {
              //這種情況下注冊,需要typeHandlerClass加上MappedJdbcTypes注解去指定jdbcType,否則,將會把jdbcType當作null進行注冊
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            /**
             * 沒有得到j(luò)avaTypeClass時,連jdbcType都不會判斷,因為typeHandlerRegistery已經(jīng)進行處理了:
             *  1、從typeHandlerClass中嘗試獲取MappedTypes注解的配置javaType,然后如果拿不到,就看看有沒有繼承TypeReference,從
             *      TypeReference的泛型T中拿到聲明的類型作為javaType,還是拿不到就用null
             *  2、從typeHandlerClass中嘗試獲取MappedJdbcTypes注解信息的jdbcType,拿不到就用null
             *  就是說就算拿不到j(luò)avaType,jdbcType也可以進行注冊TypeHandler,只不是javaType,jdbcType為null。
             */
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }

  /**
   * &lt;mapper/&gt;
   */
  private void mapperElement(XNode parent) throws Exception {
    //如果有配置mappers標簽
    if (parent != null) {
      //遍歷mappers標簽下的所有package標簽和mapper標簽
      for (XNode child : parent.getChildren()) {
        //如果child為package標簽
        if ("package".equals(child.getName())) {
          //獲取package標簽指定的映射包名路徑(相對于類路徑的資源引用)
          String mapperPackage = child.getStringAttribute("name");
          //將映射包名路徑添加到mybatis全局配置信息中
          configuration.addMappers(mapperPackage);
          //這里else表示的是mapper標簽
        } else {
          //resource表示使用相對于類路徑的資源引用
          String resource = child.getStringAttribute("resource");
          //url表示使用完全限定資源定位符(URL)
          String url = child.getStringAttribute("url");
          //使用映射器接口實現(xiàn)類的完全限定類名
          String mapperClass = child.getStringAttribute("class");
          //如果配置了resource 但是 url以及mapperClass沒有配置
          if (resource != null && url == null && mapperClass == null) {
            //設(shè)置錯誤報文實例的資源引用為resource
            ErrorContext.instance().resource(resource);
            //獲取resource文件輸入流
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //新建一個XML映射文件構(gòu)建器
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            /**
             * 解析Mapper.xml,解析mapper標簽下的所有標簽,并對解析出來的標簽信息加以封裝,
             * 然后添加到Mybatis全局配置信息中。然后重新解析Mybatis全局配置信息中未能完成解析的
             * ResultMap標簽信息,CacheRef標簽信息,DML標簽信息
             */
            mapperParser.parse();
            //如果配置了url但是resource以及mapperClass沒有配置
          } else if (resource == null && url != null && mapperClass == null) {
            //設(shè)置錯誤報文實例的資源引用為url
            ErrorContext.instance().resource(url);
            //獲取url文件輸入流
            InputStream inputStream = Resources.getUrlAsStream(url);
            //新建一個XML映射文件構(gòu)建器
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            /**
             * 解析Mapper.xml,解析mapper標簽下的所有標簽,并對解析出來的標簽信息加以封裝,
             * 然后添加到Mybatis全局配置信息中。然后重新解析Mybatis全局配置信息中未能完成解析的
             * ResultMap標簽信息,CacheRef標簽信息,DML標簽信息
             */
            mapperParser.parse();
            //如果配置了mapperClass但是resource以及url沒有配置
          } else if (resource == null && url == null && mapperClass != null) {
            //加載mapperClass對應(yīng)的java類
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            //將mapperInterface加入到mapperRegistry中
            configuration.addMapper(mapperInterface);
          } else {
            //如果把url,resource,mapperClass都配置,就會拋出異常
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

  /**
   * 是否是指定的環(huán)境
   * <p>
   *     通過 {@link XMLConfigBuilder#environment} 是否等于 {@link @id},只有{@link XMLConfigBuilder#environment}
   *     或者{@link @id}其中一個為null,都會拋出 {@link BuilderException}
   * </p>
   * @param id 環(huán)境ID
   */
  private boolean isSpecifiedEnvironment(String id) {
    if (environment == null) {
      throw new BuilderException("No environment specified.");
    } else if (id == null) {
      throw new BuilderException("Environment requires an id attribute.");
    } else if (environment.equals(id)) {
      return true;
    }
    return false;
  }

}

XPathParser

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
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;

/**
* XML解析器
 * @author Clinton Begin
 * @author Kazuki Shimizu
 */
public class XPathParser {

  private final Document document;
  private boolean validation;
  /**
   * DTD對象
   */
  private EntityResolver entityResolver;
  private Properties variables;
  private XPath xpath;

  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<>();
    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);
    }
  }

  /**
   * 檢驗mybatis-config.xml,并將mybatis-config.xml轉(zhuǎn)換成Doucument
   */
  private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);//若true解析器將DTD驗證被解析的文檔

      factory.setNamespaceAware(false);//若true解析器將提供對 XML 名稱空間的支持
      factory.setIgnoringComments(true);//若true忽略注釋
      factory.setIgnoringElementContentWhitespace(false);//若true,解析器在解析 XML 文檔時,必須刪除元素內(nèi)容中的空格
      factory.setCoalescing(false);//若true,解析器將把 CDATA 節(jié)點轉(zhuǎn)換為 Text 節(jié)點,并將其附加到相鄰(如果有)的 Text 節(jié)點
      factory.setExpandEntityReferences(true);//若true,的解析器將擴展實體引用節(jié)點

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);//設(shè)置DTD
      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);
    }
  }

  /**
   * 通用的構(gòu)造方法
   * 使用外部傳入的參數(shù),初始化成員變量;
   * 構(gòu)造XPathFactory對象,獲得Xpath的對象
   * @param validation 設(shè)置解析xml時是否對它進行校驗。
   * @param variables
   * @param entityResolver
   */
  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();
  }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容