
上一篇文章谈到了在SqlSessionFactoryBuilder中通过XMLConfigBuilder中的parse方法进行配置文件的解析,返回Configuration,这节就谈一谈XMLConfigBuilder。
构造函数
在其中共有七个构造函数,还是分为了两个流派,Reader派和InputStream派,然后通过构造函数的重载,最终只流向了一个构造函数。
/**
* 传入的流形式无论为Reader还是InputStream都将转换为XPathParser
*
* @param parser
* @param environment
* @param props
*/
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
// 日志相关
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
// 将解析标志位置为 false
this.parsed = false;
this.environment = environment;
// 设置解析器
this.parser = parser;
}
关于构造函数的方法:
- parser
- xpath解析器(可以参考xpath在java中的用法,这里就不介绍了)
- environment和props在上一篇SqlSessionFactoryBuilder介绍过了
执行流程
- 首先调用父类的构造函数,通过
new Configuration(),在父类BaseBuilder中进行一些Configuration的初始化工作(其中的细节会在分析BaseBuilder的文章中进行展开),为返回的Configuration做准备 - 执行日志的记录
- 将传入的props放入到待返回的Configuration中(这里注意,传入的props可能为null)
- 为解析标志parsed设置初始值(parsed属性的作用在下面会进行分析)
- 初始化默认连接环境
- 设置xpath解析器
属性
/** 标志是否解析过 false为没有进行解析过,true代表已经解析过 */
private boolean parsed;
/** xpath解析器 */
private final XPathParser parser;
/** environment 连接的信息 */
private String environment;
/** 在解析 properties 时使用 */
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
其中要特别说明的一点就是parsed字段,这个字段用来标记xml是否被解析过了,同一个XMLConfigBuilder只允许对配置文件解析一次,解析过后parsed将被置为true
入口方法parse()分析
/**
* 解析入口函数
*
* @return {@link Configuration} 配置信息
*/
public Configuration parse() {
if (parsed) {
// 只能进行一次解析,进行第二次解析将会抛出异常
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// xpath解析所有节点
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
在parse方法的初始执行位置就可以看到,会先对parsed进行判断,如果之前解析过了配置文件就会抛出异常,之前没有进行解析过会在解析之后将parsed置为true。
进入到parseConfiguration方法中。
/**
* 汇总解析,其中又包含了解析每种节点
*
* @param root {@link XNode}
*/
private void parseConfiguration(XNode root) {
try {
// 解析properties
propertiesElement(root.evalNode("properties"));
// 解析settings
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 加载自定义文件虚拟系统
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 解析类、包别名
typeAliasesElement(root.evalNode("typeAliases"));
// 解析plugins插件(自定义拦截器)
pluginElement(root.evalNode("plugins"));
// 创建对象的工厂,默认为DefaultObjectFactory
objectFactoryElement(root.evalNode("objectFactory"));
// 对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 加载reflectorFactory
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 配置environments
environmentsElement(root.evalNode("environments"));
// 配置databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 配置typeHandlers
typeHandlerElement(root.evalNode("typeHandlers"));
// 配置mappers
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
这个里面包含了好多个具体的解析,这就是在分块解析xml文件,每个方法解析对应的xml中的标签
每个解析方法的细节会在下一篇文章中进行详细分析
执行完parseConfiguration方法后,所有的配置信息都已经解析完毕,接下来就会返回Configuration,很多人会纳闷,这个Configuration是在哪定义的,偷偷的告诉你,Configuration定义在XMLConfigBuilder父类BaseBuilder中(这样做的作用和意义都会在分析BaseBuilder源码的文章中讲解)