阅读本文之前,请你先熟悉下 mybatis 的常规用法。
另外,本系列文章只是单纯的使用 mybatis ,没有与 spring 集成。
源码:https://github.com/coderlmm/I4mybatis
我们都知道,mybatis 的配置信息都是存储在 mybatis-config.xml 文件中的。作为配置信息肯定只需要读取一次就够了,问题是读取之后 mybatis 是如何维护的呢?很简单,mybatis 框架直接使用了一个 Configuration 对象来维护所有的配置信息。
mybatis 初始化过程
一般我们会这么获取 SqlSession。然后通过 SqlSession 获取我们的 mapper 代理对象,执行接口中的方法。
Reader re ader = Resources.getResourceAsReader( "mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sessionFactory.openSession(true);
UserDao userDao = sqlSession.getMapper(UserDao.class);
- 获取输入流;
- 获取 SqlSessionFactory;
- 获取 SqlSession;
mybatis 初始化过程就是获取 SqlSessionFactory 的过程
-
获取 SqlSessionFactory
如上图所示,mybatis 初始化主要经过以下几步:
- 调用 SqlSessionFactoryBuilder 对象的 build(Reader) 方法;
- SqlSessionFactoryBuilder 根据输入流 Reader 信息创建 XMLConfigBuilder 对象;
- SqlSessionFactoryBuilder 调用 XMLConfigBuilder 对象的 parse() 方法;
- XMLConfigBuilder 对象返回 Configuration 对象;
- SqlSessionFactoryBuilder 根据 Configuration 对象创建一个 DefaultSessionFactory 对象;
- SqlSessionFactoryBuilder 返回 DefaultSessionFactory 对象给 Client。
源代码如下
## SqlSessionFactory.java
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
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.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
从源码可以看到,在 build 函数中构建了一个 XMLConfigBuilder,下面来看看其构建过程。
-
构建 XMLConfigBuilder
从名字就可以看出这个东西是用来解析 XML 配置信息的。
XMLConfigBuilder 用来解析 mybatis 中的配置文件。
XMLMapperBuilder 用来解析 mybatis 中的映射文件。
XMLStatementBuilder 用来解析映射文件中的 SQL 语句。
他们有一个共同的父类—BaseBuilder。这个父类维护了一个全局的 Configuration 对象。在构建 XMLConfigBuilder 时会初始化 Configuration 对象。构造方法中的 XPathParser 里面封装了配置文件转化后的 Document 对象。
## XMLConfigBuilder.java
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, 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;
}
然后通过 XMLConfigBuilder 的 parse 方法解析出 XPathParser 中的 configuration 节点数据以及子节点数据。
## XMLConfigBuilder.java
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 {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(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"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
关于配置文件的解析比较复杂,后面会单独写这个。
至此,SqlSessionFactory 构建完毕。对了,其实返回的对象是 SqlSessionFactory 的子类 DefaultSqlSessionFactory。