mybatis加載配置文件生成Configuration(下)

具體解析configuration的詳細流程如下:

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

一:settings的加載解析
第三行: Properties settings = settingsAsPropertiess(root.evalNode(“settings”));
第六行: loadCustomVfs(settings);
第十二行: settingsElement(settings);

// Properties settings = settingsAsPropertiess(root.evalNode(“settings”));

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

第五行: Properties props = context.getChildrenAsProperties();從settings節點下解析出setting節點,把每一個setting節點的name和value存入properties中

public Properties getChildrenAsProperties() {
    Properties properties = new Properties();
    for (XNode child : getChildren()) {
      String name = child.getStringAttribute("name");
      String value = child.getStringAttribute("value");
      if (name != null && value != null) {
        properties.setProperty(name, value);
      }
    }
    return properties;
  }

第7-12行,校驗settings下的setting配置的name是否符合mybatis的規範,Configuration中的一些屬性

然後把setting是屬性配置到Configuration

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

二 :properties節點解析
properties本身具有屬性:resource和url用於指定資源路徑
例如:第一種(最常用)

<properties resource="mybatis.properties" > </properties>

properties還可以包含子節點:
例如:第二種

<properties>
    <property name="" value=""/>
</properties>

讀取properties代碼如下:
在這裏插入圖片描述
具體的:root.evalNode(“properties”)是從Configuration中解析出properties節點
在解析過程中:如下代碼

this.attributes = parseAttributes(node);
private Properties parseAttributes(Node n) {
    Properties attributes = new Properties();
    NamedNodeMap attributeNodes = n.getAttributes();
    if (attributeNodes != null) {
      for (int i = 0; i < attributeNodes.getLength(); i++) {
        Node attribute = attributeNodes.item(i);
        String value = PropertyParser.parse(attribute.getNodeValue(), variables);
        attributes.put(attribute.getNodeName(), value);
      }
    }
    return attributes;
  }

顯然是解析properties的屬性:其實就是resource和url

再來看上面標紅的那部分:

propertiesElement(root.evalNode("properties"));
     即==============
 private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
        //  獲取子節點的屬性
      Properties defaults = context.getChildrenAsProperties();
      // 這其實就是從上面解析出的properties的attributes中取值
      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.");
      }
      // 從這兒可以看出resource的優先級高於url ,這兒執行完成後
      // defaults 中包含子節點property的屬性同時可能包含配置文件url或者resource中配       //    置的屬性,但不要這樣用
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      // 這兒還有可能加上代碼配置的屬性
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
     // 最後總的來說:配置中包含Java代碼配置的屬性,resource的,url的,properties子節點   //配置的
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

三: typeAliases解析

<typeAliases>
     <package name=""/>
     <typeAlias type="" alias=""/>
  </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);
            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);
          }
        }
      }
    }
  }

所有的別名最後都在TypeAliasRegistry類中的map中
Map<String, Class<?>> TYPE_ALIASES = new HashMap

四:environments解析

配置:
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC">
          <property name="" value=""/>
      </transactionManager>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  
解析:
======================================
 environmentsElement(root.evalNode("environments"));
==========================================

詳細過程:
 private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
       //  獲取default屬性:從environments節點的attributes中
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      // 遍歷environments的子節點:environment
      for (XNode child : context.getChildren()) {
       // 取出子節點配置的:id
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {  // 確定默認起作用的配置id
           
           // 事務工廠
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        
          // 創建datasource
          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());
        }
      }
    }
  }

創建事務工廠具體代碼如下:已經提供默認2種實現

在這裏插入圖片描述

private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
     // 取出事務管理器的type屬性
      String type = context.getStringAttribute("type");
      // 取出子節點的屬性
      Properties props = context.getChildrenAsProperties();
      // 通過別名從TypeAliasRegistry的TYPE_ALIASES取出真正的工廠類型,創建實例
      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }

創建datasource實現細節:

private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      // datasource是type屬性
      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.");
  }```

datasource默認有2種實現
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190410220538558.png)
在創建datasource工廠的時候已經默認創建了一個datasource實例,JndiDataSourceFactory爲例:
如下:

@Override
public void setProperties(Properties properties) {
try {
InitialContext initCtx = null;
Properties env = getEnvProperties(properties);
if (env == null) {
initCtx = new InitialContext();
} else {
initCtx = new InitialContext(env);
}

  if (properties.containsKey(INITIAL_CONTEXT)
      && properties.containsKey(DATA_SOURCE)) {
    Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
    dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
  } else if (properties.containsKey(DATA_SOURCE)) {
    dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
  }

} catch (NamingException e) {
  throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
}

}
``

設置連接信息的主要細節:

  // 配置數據庫連接信息
      Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
     configuration.setEnvironment(environmentBuilder.build());

通過Environment的靜態內部類設置datasoure,事務工廠,環境id等
configuration.setEnvironment(environmentBuilder.build());種創建environment對象並設置到configuration中

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章