一、初探Configuration类

我们先来看一下MyBatis的XML配置文件的结构,(摘自mybatis.org)

MyBatis学习笔记(三) Configuration类

下面这个是Configuration类的部分变量

MyBatis学习笔记(三) Configuration类

一点不一样是不是???

其实Configuration类是由XMLConfigBuilder(继承自BaseBuilder类)解析而来的,由如下方法(parseConfiguration)解析

  private void parseConfiguration(XNode root) {
    try {
      Properties settings = settingsAsPropertiess(root.evalNode("settings"));
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectionFactoryElement(root.evalNode("reflectionFactory"));
      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);
    }
  }

该方法的调用者是同一个类的parse()方法,在第一篇笔记里面就提到过,SqlSessionFactoryBuilder类负责构建SqlSessionFactory,这其中重要的一步就是解析配置文件,调用的正是这个parse()方法,调用链如下:

MyBatis学习笔记(三) Configuration类

MyBatis学习笔记(三) Configuration类

 

回到parseConfiguration方法,该方法负责将XML文件中的信息解析到Configuration类的变量中,使其一一对应起来,下面是最后一个方法mapperElement(root.evalNode("mappers"))的实现,负责读取<mappers>节点,其他的方法也是同样的作用

MyBatis学习笔记(三) Configuration类

Configuration类就像是MyBatis的总管,里面包含了所有的信息,有一些属性不设置也不会影响configuration的构建,因为MyBatis会给这些属性赋上默认值,以保证MyBatis能够正常运行。

 二、各个配置解析

 1. properties全局参数

官方样例

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

properties参数由propertiesElement()方法进行解析,方法实现如下:
private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      //注意,resource属性和url属性不能同时存在,否则将抛出无法解析的异常
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      //后面读取的属性会覆盖原来已有的,因为properties继承自HashTable,还是键值对,后put的值会覆盖之前put进来的值
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //如果Configuration对象中variables属性不为空,则将其添加到properties对象中
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      //最后将这些参数保存至Configuration对象中
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

根据以上信息我们可以得出:

通过代码设置的configuration参数的优先级最高,因为他是在方法的最后面将那些值put进去的,然后就是properties子节点的优先级最低,最先被记载,也最容易被后面的参数覆盖,所以,如果采取读取外部文件的方式(resource或者url方式),子节点最好不要和其他参数重复。

 

2.settings

<settings>
  <!-- 开启二级缓存 -->
  <setting name="cacheEnabled" value="true" />
  <!-- 开启延迟加载 -->
  <setting name="lazyLoadingEnabled" value="true" />

  <!--当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载,反之,每种属性都会按需加载-->

  <setting name="aggressiveLazyLoading" value="true" />
</settings>

settings先是由settingsAsPropertiess()方法解析成properties对象,然后再由loadCustomVfs()方法和settingsElement()方法分别解析,实现如下:
//这个方法用来将settings节点解析成properties对象
private Properties settingsAsPropertiess(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))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

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

//处理每一个设置子节点,如果没有设置则赋上默认值
private void settingsElement(Properties props) throws Exception {
    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"), true));
    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.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
  }

3.typeAliases别名
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
它还有一个 <package name="com.ys.po" /> 标签,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名
配置了类型别名之后我们使用resulttype="Alias"就不用些全限定名了,非常的方便。
我们可以自己定义别名,这个大部分是自己的实体类,MyBatis也为我们默认设置了一下别名(大部分在TypeAliasRegistry.class中),包括常见的int,byte等类型,还有settings里面的一些value值,比如LRU等,详细的别名设置可以参考官方,官网给出的非常详细了,再次不再赘述,下面贴一下Configuration构造方法设置的别名。
public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }
View Code

相关文章:

  • 2021-11-25
  • 2021-06-16
  • 2022-12-23
  • 2021-04-15
  • 2021-12-19
  • 2021-06-02
猜你喜欢
  • 2021-09-15
  • 2021-06-22
  • 2021-09-13
  • 2021-12-01
  • 2022-12-23
  • 2021-10-21
  • 2021-08-30
相关资源
相似解决方案