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-事务对象源码解析
  4. Mybatis-数据源源码解析
  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源码解析]https://www.jianshu.com/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编码获得资源来构建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配置文件进行解析的解析器对应配置文件先进性解析封装
   * @param inputStream 配置文件文件流
   * @param environment 加载哪种环境(开发环境/生产环境),包括数据源和事务管理器
   * @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 {
        //关闭配置文件文件流
        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}以','分隔的形式转换成{@link Set},如果{@link @value}为null,就用{@link @defaultValue}
   */
  protected Set stringSetValueOf(String value, String defaultValue) {
    value = value == null ? defaultValue : value;
    return new HashSet<>(Arrays.asList(value.split(",")));
  }

  /**
   * 找出对应{@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);
    }
  }

  /**
   * 找出对应{@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);
    }
  }

  /**
   * 找出对应{@link @alias}的{@link ParameterMode},若{@link @alias}为null返回null,若没有找到就会抛出{@link BuilderException},
   * 

ParameterMode: {@link ParameterMode#IN},{@link ParameterMode#OUT},{@link ParameterMode#INOUT}

*/ 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); } } /** * 创建{@link @alias}的实例,{@link @alias}为null返回null,若在创建抛出任何异常信息都会封装到{@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解析出对应的类 实际调用{@link #resolveAlias(String)},返回类实例 */ protected Class resolveClass(String alias) { if (alias == null) { return null; } try { return resolveAlias(alias); } catch (Exception e) { throw new BuilderException("Error resolving class. Cause: " + e, e); } } /** * 构建TypeHandler,不没有注册到{@link #typeHandlerRegistry},只是返回TypeHandler实例对象,实际调用{@link #resolveTypeHandler(Class, Class)} * @param javaType 可以不传的,不传就用typeHandlerAlias的无参构造方法,传就尝试调用带有接收javaType的构造方法[p:即使调用失败也不会抛出异常,而是调用无参构造方法] * @param typeHandlerAlias 若null,该方法直接返回null;若传入的包+类没有继承{@link TypeHandler}会抛出{@link BuilderException} */ protected TypeHandler resolveTypeHandler(Class javaType, String typeHandlerAlias) { if (typeHandlerAlias == null) { return null; } //获取typeHandlerAlias对应的类 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 已经严重type是TypeHandler类。 Class> typeHandlerType = (Class>) type; return resolveTypeHandler(javaType, typeHandlerType); } /** * 构建TypeHandler,没有注册到{@link #typeHandlerRegistry},只是返回TypeHandler实例对象 * @param javaType 可以不传的,但是不传,也是可以的,但是不建议。 * @param typeHandlerType 若为null,该方法直接返回null * @return */ protected TypeHandler resolveTypeHandler(Class javaType, Class> 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找出对应的类 */ protected Class 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运行时所用的数据库环境ID,一般从 <environments/> 的 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 加载哪种环境(开发环境/生产环境),包括数据源和事务管理器
   * @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对应的XML解析器
   * @param environment 加载哪种环境(开发环境/生产环境),包括数据源和事务管理器
   * @param props 属性配置文件,那些属性可以用${propName}语法形式多次用在配置文件中
   */
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //新建一个Mybatis全局配置信息类对象
    super(new Configuration());
    //设置错误报文实例的资源引用
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);//设置属性文件的变量到配置类configuration
    this.parsed = false;//parse表示是否已经解析了
    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;
    //对应节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  /**
   * 解析mybatis-config.xml所有标签信息,并实例化标签对应的对象,配置进 {@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);
    }
  }

  /**
   * 读取便签信息,每个的name属性对应着Configuration的属性变量名,
   * 该放会检验标签的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))) {//这里就是判断有没有对应的属性在configuration里,没有就抛出异常
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

  /**
   * 加载虚拟文件系统配置,读取服务器资源
   * VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源
   * Mybatis中提供了VFS这个配置,主要是通过该配置可以加载自定义的虚拟文件系统应用程序
   */
  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 vfsImpl = (Class)Resources.classForName(clazz);
          configuration.setVfsImpl(vfsImpl);
        }
      }
    }
  }

  /**
   * 制定日志的实现(log4j等)
   */
  private void loadCustomLogImpl(Properties props) {
    Class logImpl = resolveClass(props.getProperty("logImpl"));//找出logImpl对应的日志类
    configuration.setLogImpl(logImpl);
  }

  /**
   * 类型别名元素
   * @param parent 
   */
  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);//通过类加载器加载对应的来
            //注册别名到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);
          }
        }
      }
    }
  }

  /**
   * 元素,每个插件就是mybatis的拦截器。
   * @param parent 标签
   */
  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();//加载对应的拦截类,并new一个实例
        interceptorInstance.setProperties(properties);//加上标签内的标签信息
        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();//加载对应的ObjectFactory类,并new一个实例
      factory.setProperties(properties);//加上标签内的标签信息
      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();//加载对应的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();//加载对应的ReflectorFactory类,并new一个实例
      configuration.setReflectorFactory(factory);//注册到ReflectorFactory
    }
  }

  /**
   * 读取标签的属性变量,以及合并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属性在标签中只能指定一个
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      //加载对应路径的属性
      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);//设置解析器的属性变量
      configuration.setVariables(defaults);//设置Configuration的属性变量
    }
  }

  /**
   * 将标签的设置全部设置进去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")));
  }

  /**
   * <Environments/>标签元素
   * 

* 获取<Environments/>下的default属性取得当前Mybatis所需要的环境ID并赋值给 {@link XMLConfigBuilder#environment}, * 然后获取<environment/>的id属性(环境ID)并赋值给{@link @id},判断{@link @id}是不是{@link XMLConfigBuilder#environment}, * 是就做以下操作 *

    *
  1. 获取环境ID
  2. *
  3. 获取事务管理器工厂的实例对象,赋值给 {@link @txFactory}
  4. *
  5. 获取数据库数据源工厂的实例对象{@link @dsFactory},然后从{@link @dsFactory} 获取数据源,赋值给 {@link @dataSoure}
  6. *
  7. 传入{@link @txFactory},{@link @dataSoure},{@link @id} 构建 {@link Environment} 传入 {@link XMLConfigBuilder#configuration}
  8. *
*

* @param context * @throws Exception */ private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default");//默认环境ID,指的ID属性 } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id");//环境ID //只有符合指定的默认环境ID的环境ID才会被解析 if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//事务管理器工厂 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//数据源工厂 DataSource dataSource = dsFactory.getDataSource();//取出数据源 //构建环境 Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } } /** * <databaseIdProvider/> 解析。【数据库厂商标识】 *

* MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 * MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 * 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 * 为支持多厂商特性设置 databaseIdProvider *

*

* Mybatis内置了一个 {@link org.apache.ibatis.mapping.VendorDatabaseIdProvider},对type属性设置别名'DB_VENDOR'即可使用。 *

* 参考文档 *
    *
  1. https://www.cnblogs.com/hellowhy/p/9676037.html
  2. *
  3. http://www.mybatis.org/mybatis-3/zh/configuration.html#databaseIdProvider
  4. *
*/ 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中获取数据源,交给databaseIdProvider获取数据库ID。 * 数据ID,其实就是数据库名,或者properties对应key的value(这个需要用户去配置) */ Environment environment = configuration.getEnvironment(); if (environment != null && databaseIdProvider != null) { String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); configuration.setDatabaseId(databaseId); } } /** * 下的标签信息,返回事务工厂 {@link TransactionFactory} *
    *
  • type= 'JDBC' ,返回 {@link org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory}
  • *
  • type= 'MANAGED' ,返回 {@link org.apache.ibatis.transaction.managed.ManagedTransactionFactory}
  • *
  • type='包+类名',返回对应类实例 {@link TransactionFactory} ,该类必须保证继承了 {@link TransactionFactory} 接口
  • *
* @throws BuilderException 没有设置就会抛出异常 */ 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."); } /** * 下的标签信息,返回数据源工厂 {@link DataSourceFactory} *
    *
  • type= 'JNDI' ,返回 {@link org.apache.ibatis.datasource.jndi.JndiDataSourceFactory}
  • *
  • type= 'POOLED' ,返回 {@link org.apache.ibatis.datasource.pooled.PooledDataSourceFactory}
  • *
  • type= 'UNPOOLED' ,返回 {@link org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory}
  • *
  • type='包+类名',返回对应类实例 {@link DataSourceFactory} ,该类必须保证继承了 {@link DataSourceFactory} 接口
  • *
* @throws BuilderException 没有设置就会抛出异常 */ 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."); } /** *<typeHandler/>解释 *

* 调用{#typeHandlerRegistry}注册TypeHandler。就算javaType,jdbcType都是null,都能注册得到TypeHandler,只不过javaType,jdbcType都是null * 。但是我不建议这样做、 *

* @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 { /** * 没有得到javaTypeClass时,连jdbcType都不会判断,因为typeHandlerRegistery已经进行处理了: * 1、从typeHandlerClass中尝试获取MappedTypes注解的配置javaType,然后如果拿不到,就看看有没有继承TypeReference,从 * TypeReference的泛型T中拿到声明的类型作为javaType,还是拿不到就用null * 2、从typeHandlerClass中尝试获取MappedJdbcTypes注解信息的jdbcType,拿不到就用null * 就是说就算拿不到javaType,jdbcType也可以进行注册TypeHandler,只不是javaType,jdbcType为null。 */ typeHandlerRegistry.register(typeHandlerClass); } } } } } /** * <mapper/> */ 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"); //使用映射器接口实现类的完全限定类名 String mapperClass = child.getStringAttribute("class"); //如果配置了resource 但是 url以及mapperClass没有配置 if (resource != null && url == null && mapperClass == null) { //设置错误报文实例的资源引用为resource ErrorContext.instance().resource(resource); //获取resource文件输入流 InputStream inputStream = Resources.getResourceAsStream(resource); //新建一个XML映射文件构建器 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) { //设置错误报文实例的资源引用为url ErrorContext.instance().resource(url); //获取url文件输入流 InputStream inputStream = Resources.getUrlAsStream(url); //新建一个XML映射文件构建器 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对应的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."); } } } } } /** * 是否是指定的环境 *

* 通过 {@link XMLConfigBuilder#environment} 是否等于 {@link @id},只有{@link XMLConfigBuilder#environment} * 或者{@link @id}其中一个为null,都会抛出 {@link BuilderException} *

* @param id 环境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 evalNodes(String expression) {
    return evalNodes(document, expression);
  }

  public List evalNodes(Object root, String expression) {
    List 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转换成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 文档时,必须删除元素内容中的空格
      factory.setCoalescing(false);//若true,解析器将把 CDATA 节点转换为 Text 节点,并将其附加到相邻(如果有)的 Text 节点
      factory.setExpandEntityReferences(true);//若true,的解析器将扩展实体引用节点

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

  /**
   * 通用的构造方法
   * 使用外部传入的参数,初始化成员变量;
   * 构造XPathFactory对象,获得Xpath的对象
   * @param validation 设置解析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();
  }

}

你可能感兴趣的:(Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码分析)