概述
XMLConfigBuilder 负责加载 mybatis-config.xml 中的配置信息到 Configuration,主要通过 XMLConfigBuilder#parse() 方法:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
其中详细的加载步骤在 parseConfiguration 方法中:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 加载properties节点,一般是定义一些变量
propertiesElement(root.evalNode("properties"));
// 加载一些设置
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 加载别名
typeAliasesElement(root.evalNode("typeAliases"));
// 加载插件
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 加载Mapper的配置文件,底层通过 XMLMapperBuilder 加载,最主要的有两个:一个是sql的定义,一个是resultMap
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
加载 Properties 节点
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 先加载property子节点下的属性
Properties defaults = context.getChildrenAsProperties();
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.");
}
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);
}
parser.setVariables(defaults);
// 设置了变量列表中去
configuration.setVariables(defaults);
}
}
从这个方法中可以看出配置规则: 1.可以设置url或resource属性从外部文件中加载一个properties文件 2.可以通过property子节点进行配置,如果子节点属性的key与外部文件的key重复的话,子节点的将被覆 3.通过编程方式定义的属性最后加载,优先级最高: public SqlSessionFactory build(InputStream inputStream, Properties properties)
properties 配置示例,这里加载的主要是给后面的配置作为变量使用:
加载 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 源码,发现mybatis已经为定义了很多别名,方便以后的配置:
public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
}
引用链接
《深入理解mybatis原理》 Mybatis初始化机制详解
Mybatis 源码分析
要下班了?扫一扫,地铁上阅读 :)
生活只有眼前的苟且,哪有诗和远方 :(