简述
在我们刚学习Mybatis,没有集成Spring时,通常会以下面这种形式来进行SqlSessionFactory的创建
SqlSessionFactory sqlSessionFactory = null;
String resource = "mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
通俗的讲就是使用SqlSessionFactoryBuilder()的build()方法,并传入配置文件来生产SqlSessionFactory,下面我们通过阅读源码简要的了解以下整个流程
流程分析
【SqlSessionFactoryBuilder】
首先直接进入SqlSessionFactoryBuilder的build方法
在SqlSessionFactoryBuilder中,实现了多种不同的build方法,但大致可分为三类
- 以字符流(Reader)的形式读取配置文件
//核心,其他参数为Reader的方法都会最终指向本方法
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) {
// Intentionally ignore. Prefer previous error.
}
}
}
- 以字节流(InputStream)的形式读取配置文件
//核心,其他参数为InputStream的方法都会最终指向本方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
- 直接以Configuration类实例作为入参
本方法是最核心的build方法,以上两个方法最终都会指向本方法
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
通过上面三段代码我们可以了解到SqlSessionFactory构建的基本流程

- 可以发现,在构建SqlSessionFactory时,最为重要的还是Configuration的创建
- 其实我们完全可以通过代码的方式去创建Configuration类的实例对象,然后直接调用public SqlSessionFactory build(Configuration config)方法去创建,但是这样不管是可读性还是修改代码的简易程度都不如XML配置,因此我们选择使用流的方式去读取XML配置文件然后解析为Configuration类
- 在从流到Configuration的过程中XMLConfigBuilder起到了至关重要的作用
【XMLConfigBuilder】
在SqlSessionFactory的build方法中,通过如下手段产生Configuration类
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
parser.parse();//parse方法生产Configuration实例对象
我们进入源码查看流程
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
Properties settings = settingsAsPropertiess(root.evalNode("settings"));
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
- 大致流程就是XMLConfigBuilder读取配置文件流后,将其转为XPathParser,XPathParser调用parse方法(调用parseConfiguration方法)
- parseConfiguration中完成了将配置文件中的内容装载到Configuration中,其中调用的各个方法内部最终都会使用configuration.setXXX的形式,将对应属性进行装载
建造者(Builder)模式
在整个SqlSessionFactory构建过程中,我们不难发现,其实就是如下思路
- 构建SqlSessionFactory需要构建大量的对象属性,如environments、typeHandlers等等,构建这些内容也异常复杂,因此Mybatis提供了一个配置类Configuration对这些对象来进行统筹规划,分步构建
- Configuration中所有内容构建完毕后,调用构造器XMLConfigBuilder和SqlSessionFactoryBuilder进行构造生产SqlSessionFactory
以上的设计模式被称为建造者(Builder)模式,可以将一个产品的内部表象(属性)与产品的生产过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象
【如有错误,请指出!O(∩_∩)O谢谢】
网友评论