Mybatis源码分析(4)---配置节点settings源码解析

  1. 背景
    在上篇Mybatis源码分析(3)—配置节点properties源码解析中,我们通过解析properties节点在于XMLConfigBuilder类中的propertiesElement(XNode context)方法源码,了解了整个properties节点的解析过程。
    今天接下来我们将对节点settings进行源码分析,分析之前,依然先通过以下图来重温一下之前SqlSessionFactory对象创建的过程:
    Mybatis源码分析(4)---配置节点settings源码解析_第1张图片
    当然,我们依然围绕着Demo工程mybatisCode对节点settings进行深入的解析和测试
  2. 节点settings介绍
    这里我们就不进行过多的介绍,在Mybatis官网中,对settings节点的描述为:MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。配置完整的 settings 元素内容有以下:
<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

每个setting元素设置的详细描述,大家可以去官网中mybatis - Mybatis3 | 配置 设置(settings)直接学习了解:
Mybatis源码分析(4)---配置节点settings源码解析_第2张图片
3. 节点settings源码

  • 首先我们先来看看节点settings源码的位置:XMLConfigBuilder类中的parseConfiguration(XNode root)方法中
  private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties"));
      
      //解析节点
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      //解析后settings配置作为参数传入loadCustomVfs方法
      loadCustomVfs(settings);
      //解析后settings配置作为参数传入loadCustomLogImpl方法
      loadCustomLogImpl(settings);
      
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      
      //解析后settings配置作为参数传入settingsElement方法
      settingsElement(settings);
      
      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);
    }
  }

通过上面源码我们,大概了解了settings节点的解析过程:先加载整个settings的元素到集合Properties中,然后再把带着Properties集合传递到具体的方法中,进行具体的处理,那整个源码具体详细的解析过程是什么样子的呢?
接下来我们将按照上面settings解析过程,深入到源码方法中去窥探。

  • Properties settingsAsProperties(XNode context)源码解析
    根据方法名和返回参数,我们能知道mybatis把settings节点元素加载了Properties集合配置类中:
  private Properties settingsAsProperties(XNode context) {
  	//如果mybatis-config.xml没有配置节点,则返回一个空的Properties配置类集合
    if (context == null) {
      return new Properties();
    }
    //getChildrenAsProperties方法在XNode类中,下面的XNode类中该方法的源码,我们可以了解一下
    // 把节点中的所有元素的name和value属性值已K-V形式设置到Properties配置类中
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    // 直接翻译上面源码的因为注释:检查全局配置类Configuration是否知道所有settings设置
    // MetaClass类中作用就是把Configuration的所有属性的命名封装为起来
    // 然后对元素中的name进行遍历判断是否非Configuration全局配置类中的属性名称,存在非法的属性名称进行异常抛出
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    //遍历所有元素解析后name属性值,既Properties props中的Key
    for (Object key : props.keySet()) {
      //判断key是否存在,不存在则抛出BuilderException异常
      //这里提醒一下大家,如果你启动mybatis出现下面的异常msg,可以直接就定位到settings节点中某个元素setting的name属性值配置错误了
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

XNode类中getChildrenAsProperties() 方法源码

  public Properties getChildrenAsProperties() {
    Properties properties = new Properties();
    //遍历节点的元素
    for (XNode child : getChildren()) {
      //获取元素的name属性的字符串值
      String name = child.getStringAttribute("name");
      //获取元素的value属性的字符串值
      String value = child.getStringAttribute("value");
      if (name != null && value != null) {
      	//name属性值作为key,value属性值作为value,存放到Properties集合中
        properties.setProperty(name, value);
      }
    }
    return properties;
  }

Configuration类中的属性名称,与上面官网截图的设置名一一对应,以后我们有机会将单独对Configuration类进行源码分析介绍
Mybatis源码分析(4)---配置节点settings源码解析_第3张图片

  • loadCustomVfs(settings)方法源码分析----指定VFS 的实现
    VFS :虚拟文件系统,主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源,通过该配置可以加载自定义的虚拟文件系统应用程序,具体了解VFS可以通过文章《什么是vfs以及vfs的作用》阅读
  private void loadCustomVfs(Properties props) throws ClassNotFoundException {
    //获取元素中name属性为vfsImpl的value值
    String value = props.getProperty("vfsImpl");
    if (value != null) {
      //自定义VFS的实现的类全限定名,以逗号分隔
      String[] clazzes = value.split(",");
      for (String clazz : clazzes) {
        if (!clazz.isEmpty()) {
          //利用反射获取VFS实现类的Class对象
          @SuppressWarnings("unchecked")
          Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
          //最后注册到全局配置类Configuration中的vfsImpl容器中
          configuration.setVfsImpl(vfsImpl);
        }
      }
    }
  }
  • loadCustomLogImpl(settings)方法源码分析
    指定 MyBatis 所用日志的具体实现,未指定时将自动查找
    指定的类型有:SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
  private void loadCustomLogImpl(Properties props) {
    Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
    //最后注册到全局配置类Configuration中的logImpl容器中
    configuration.setLogImpl(logImpl);
  }
  • settingsElement(settings)源码分析
    调用该方法之后,mybatis会把settings节点的所有设置挨个的注册到全局配置类Configuration中的各个属性容器,此时settings节点的解析基本结束,把各个seting元素的中的设置的东西别注册到了Configuration中
  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")));
  }
  1. 结尾
    以上就是整个mybatis-config.xml中节点settings在mybatis初始化过程中解析的过程。
    通过此篇文章,我们进一步的了解到全局配置类Configuration的强大,可以说它是mybatis的核心,整个mybatis的运行所需要的东西均来源它,而它的组成来源于各个节点的解析。
    对于节点settings设置的解析,有很多元素非常有用,具体大家可以根据官网提供的setting元素需要进行测试

你可能感兴趣的:(Mybatis)