MyBatis 3 解析mybatis-config.xml配置
按照惯例,直接上干货。
MyBatis初始化工作包括加载和解析mybatis-config.xml配置文件、映射文件和相关注解信息。初始化入口是SqlSessionFactoryBuider.build()方法。
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
}
}
}
XMLConfigBuilder对象继承BaseBuider抽象类,用来解析mybatis-config.xml配置文件。而BaseBuider类中主要包括3个配置项:
public abstract class BaseBuilder {
protected final Configuration configuration; //全局唯一配置对象
protected final TypeAliasRegistry typeAliasRegistry; //解析mybatis-config.xml中标签
protected final TypeHandlerRegistry typeHandlerRegistry; //解析mybatis-config.xml中标签
}
其中TypeAliasRegistry和TypeHandlerRegistry在Configuration初始化时配创建出来,也是全局唯一配置:
public class Configuration {
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
}
当然,Configuration类中并不是只有以上2项配置,实际上MyBatis的配置都存放在这里,这里就不一一展开说了。
MyBatis的初始化是在XMLConfigBuilder类中完成的,由其负责解析mybatis-config.xml配置文件,其核心属性包括:
public class XMLConfigBuilder extends BaseBuilder {
//是否已经解析过mybatis-config.xml文件
private boolean parsed;
//用来解析mybatis-config.xml文件的对象
private final XPathParser parser;
//标记标记的名称,默认读取default属性
private String environment;
//用来创建和缓存Reflector对象
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
}
解析方法:
//解析配置
public Configuration parse() {
//只解析加载一次
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//根节点是configuration
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//解析配置中各个节点
private void parseConfiguration(XNode root) {
try {
//1.解析节点
propertiesElement(root.evalNode("properties"));
//2.解析节点
typeAliasesElement(root.evalNode("typeAliases"));
//3.解析节点
pluginElement(root.evalNode("plugins"));
//4.解析节点
objectFactoryElement(root.evalNode("objectFactory"));
//5.解析节点
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//6.解析节点
settingsElement(root.evalNode("settings"));
//7.解析节点
environmentsElement(root.evalNode("environments"));
//8.解析节点
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//9.解析节点
typeHandlerElement(root.evalNode("typeHandlers"));
//10.解析节点
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
对应官方给出的配置项:

Mybatis配置各属性含义及赋值就不在这里赘述了。以解析
private void propertiesElement(XNode context) throws Exception {
//
//
//
//
if (context != null) {
//如果在这些地方,属性多于一个的话,MyBatis 按照如下的顺序加载它们:
//1.读取property属性。
//2.读取url或resource对应的属性,第19行指明二者不能同时配置。
//3.作为方法参数传递的属性最后被读取
//如果属性名称相同,后加载的值会覆盖前者
//1.XNode.getChildrenAsProperties函数方便得到孩子所有Properties
Properties defaults = context.getChildrenAsProperties();
//2.然后查找resource或者url,加入前面的Properties
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("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));
}
//3.Variables也全部加入Properties
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
其他大多数配置的加载过程跟上面过程相似,也比较好理解,鉴于篇幅原因建议大家看看源码,后面文章还会讲一下解析