具體解析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中