Mybatis 是如何解析配置文件中的内容 – settings
文章目录
提示:Mbatis 的加载入口可以看这篇文章:
【Mybatis 源码】 Mybatis 是如何解析配置文件中的内容 – properties
一、解析 settings 节点
<settings>
节点在解析的过程中分为了几块内容:
// 获取配置文件中设置的选项
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 加载 VFS
loadCustomVfs(settings);
// 加载日志配置选项
loadCustomLogImpl(settings);
// 给全部配置选项设置默认值或者用户自定义的值
settingsElement(settings);
1. 使用方式
设置项目参考链接:
【Mybatis 使用】mybatis-config.xml 配置(properties 和 settings)
因为配置项比较多,而且有些配置项平常基本使用不到,所以这边不详细讲解,只挑选一些典型的作为例子,具体的可以等到用到的时候再配置。
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 开启延迟加载的功能 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 即按需加载属性 -->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 开启对数据库生成主键的支持 -->
<setting name="useGeneratedKeys" value="false" />
</settings>
2. 获取配置文件中的选项
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
// 获取文件中的配置的选项
Properties props = context.getChildrenAsProperties();
// 判断设置的选项是否是属于已知的选项,如果不是已知选项中的一员,则会抛出异常
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
hrow new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
3. 加载 vfs 组件
vfs(虚拟化文件系统),平常基本上用不到这个组件,所以只需要了解一下就行。
private void loadCustomVfs(Properties props) throws ClassNotFoundException {
// 获取配置文件中配置的 vfs 自定义类
String value = props.getProperty("vfsImpl");
if (value != null) {
// 配置文件中定义的 vfs 可以用逗号来分隔,所以按照逗号来获取多个自定义类
String[] clazzes = value.split(",");
for (String clazz : clazzes) {
// 判断clazz是否为空
if (!clazz.isEmpty()) {
// 获取类名
@SuppressWarnings("unchecked")
Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
// 在全局配置中配置 vfs 等待后面使用
configuration.setVfsImpl(vfsImpl);
}
}
}
}
4. 加载日志配置
日志配置会先从预设好的值中获取相对应的类,比如在官方文档中,日志的选项有这么几种
SLF4J
、
LOG4J(3.5.9 起废弃)
、
LOG4J2
、
JDK_LOGGING
、
COMMONS_LOGGING
、
STDOUT_LOGGING
、
NO_LOGGING
,这些选项都是各个日志类的别名,在实际使用过程中,用户会先从这些别名中挑选自己需要的日志类,然后等待 Mybatis 加载此日志类就可以使用。
比如如果在设置的时候使用了
SLF4J
,那在 Mybatis 加载的时候会根据
SLF4J
去找到 Slf4jImpl.class 这个日志来作为整个框架的日志类。
- Mybatis 加载日志类的过程:
private void loadCustomLogImpl(Properties props) {
// 获取和配置名相匹配的日志类
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
// 加载获取到的日志类
configuration.setLogImpl(logImpl);
}
【1】获取配置的类
获取配置类的过程其实就是从预设置的各种日志类获取相匹配的类的过程。
- BaseBuilder#resolveClass():
protected <T> Class<? extends T> resolveClass(String alias) {
if (alias == null) {
return null;
}
try {
// 获取别名对应的类
return resolveAlias(alias);
} catch (Exception e) {
throw new BuilderException("Error resolving class. Cause: " + e, e);
}
}
- BaseBuilder#resolveAlias():
protected <T> Class<? extends T> resolveAlias(String alias) {
return typeAliasRegistry.resolveAlias(alias);
}
- TypeAliasRegistry#resolveAlias():
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {
return null;
}
// 转换为小写
String key = string.toLowerCase(Locale.ENGLISH);
Class<T> value;
// 如果预设的值包含了此值,则会获取此值对应的类
if (typeAliases.containsKey(key)) {
value = (Class<T>) typeAliases.get(key);
} else {
// 否则会用 Resources 加载对应的类
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
在加载过程中预设的值都是在 Configuration 类初始化的过程中进行设置的,下面的代码就是上面文档中那七种日志类预设置的过程。
public Configuration() {
// 给各种日志类设置别名,方便用户设置
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);
}
【2】LogFactory 设定自定义的日志类
设置类的过程其实就是在前面第一步获取日志类成功以后,交给 LogFactory 日志框架进行加载。
public void setLogImpl(Class<? extends Log> logImpl) {
if (logImpl != null) {
this.logImpl = logImpl;
LogFactory.useCustomLogging(this.logImpl);
}
}
5. 加载全部的 setting 设置
等到上面两个自定义的设置全部处理好以后,最后就是来到了加载
<settings>
标签的最后一步,就是将配置文件中设置的所有的自定义框架设置进行加载,如果在文件中有些框架设置没有单独配置,则会使用默认的值来设置。
比如在最开始的时候,在配置文件中设置了四项需要的框架配置,那在加载时候,会将 value 设置的值填充并加载。
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 开启延迟加载的功能 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 即按需加载属性 -->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 开启对数据库生成主键的支持 -->
<setting name="useGeneratedKeys" value="false" />
</settings>
【1】加载过程
在加载过程中可以看到所有框架配置的选项,以及每个选项的默认值。
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.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
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")));
configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
configuration.setArgNameBasedConstructorAutoMapping(booleanValueOf(props.getProperty("argNameBasedConstructorAutoMapping"), false));
configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
configuration.setNullableOnForEach(booleanValueOf(props.getProperty("nullableOnForEach"), false));
}
二、参考链接