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