提示:Mbatis 的加载入口可以看这篇文章:【Mybatis 源码】 Mybatis 是如何解析配置文件中的内容 – properties
节点在解析的过程中分为了几块内容:
// 获取配置文件中设置的选项
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 加载 VFS
loadCustomVfs(settings);
// 加载日志配置选项
loadCustomLogImpl(settings);
// 给全部配置选项设置默认值或者用户自定义的值
settingsElement(settings);
设置项目参考链接:【Mybatis 使用】mybatis-config.xml 配置(properties 和 settings)
因为配置项比较多,而且有些配置项平常基本使用不到,所以这边不详细讲解,只挑选一些典型的作为例子,具体的可以等到用到的时候再配置。
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;
}
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); } } }
}
日志配置会先从预设好的值中获取相对应的类,比如在官方文档中,日志的选项有这么几种 SLF4J、LOG4J(3.5.9 起废弃)、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,这些选项都是各个日志类的别名,在实际使用过程中,用户会先从这些别名中挑选自己需要的日志类,然后等待 Mybatis 加载此日志类就可以使用。
比如如果在设置的时候使用了 SLF4J,那在 Mybatis 加载的时候会根据 SLF4J 去找到 Slf4jImpl.class 这个日志来作为整个框架的日志类。
private void loadCustomLogImpl(Properties props) {// 获取和配置名相匹配的日志类Class extends Log> logImpl = resolveClass(props.getProperty("logImpl"));// 加载获取到的日志类configuration.setLogImpl(logImpl);
}
获取配置类的过程其实就是从预设置的各种日志类获取相匹配的类的过程。
protected 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);}
}
protected Class extends T> resolveAlias(String alias) { return typeAliasRegistry.resolveAlias(alias);
}
public Class resolveAlias(String string) { try { if (string == null) { return null; } // 转换为小写String key = string.toLowerCase(Locale.ENGLISH); Class value;// 如果预设的值包含了此值,则会获取此值对应的类if (typeAliases.containsKey(key)) { value = (Class) typeAliases.get(key); } else {// 否则会用 Resources 加载对应的类value = (Class) 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);
}
设置类的过程其实就是在前面第一步获取日志类成功以后,交给 LogFactory 日志框架进行加载。
public void setLogImpl(Class extends Log> logImpl) { if (logImpl != null) { this.logImpl = logImpl; LogFactory.useCustomLogging(this.logImpl); }
}
等到上面两个自定义的设置全部处理好以后,最后就是来到了加载 标签的最后一步,就是将配置文件中设置的所有的自定义框架设置进行加载,如果在文件中有些框架设置没有单独配置,则会使用默认的值来设置。
比如在最开始的时候,在配置文件中设置了四项需要的框架配置,那在加载时候,会将 value 设置的值填充并加载。
在加载过程中可以看到所有框架配置的选项,以及每个选项的默认值。
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));
}