1. 官方文档理解的重要性
一个好的程序员应该学会如何阅读官方文档,一个好的官方文档应该是一个简单清晰,很准确的告诉读者该产品具备什么、解决什么,笔者认为MyBatis就是具备这样优点的产品。本文一改其他文章一上来贴几个架构图把读者砸晕的方式,从官方文档来学习MyBatis源码。
首先打开 官方网址,一个优秀的产品应该是世界的,发现支持中文,果断换中文版。 例行下载源码,开始进入入门章节。
入门中提到
“从xml中构建 SqlSessionFactory”
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
从 SqlSessionFactory 中获取 SqlSession
既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
探究已映射的 SQL 语句
现在你可能很想知道 SqlSession 和 Mapper 到底具体执行了些什么操作,但 SQL 语句映射是个相当广泛的话题,可能会占去文档的大部分篇幅。 但为了让你能够了解个大概,这里会给出几个例子。
提到几个“大佬”级别的类,通通拿小本子记下:
- SqlSessionFactory
- SqlSessionFactoryBuilder
- Configuration
- SqlSession
- Mapper(可能的名字xxxMapper 或者MapperXXX之类,待确认)
阅读Java API章节 SqlSession
使用 MyBatis 的主要 Java 接口就是 SqlSession。你可以通过这个接口来执行命令,获取映射器示例和管理事务。在介绍SqlSession 接口之前,我们先来了解如何获取一个 SqlSession 实例。SqlSessions 是由SqlSessionFactory 实例创建的。SqlSessionFactory 对象包含创建 SqlSession 实例的各种方法。而SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 创建的,它可以从 XML、注解或 Java配置代码来创建 SqlSessionFactory。
通篇阅读后,可以大致推断出SqlSession为Mybatis接口层。构建架构图: 我们第一步能明确SqlSession为接口层,其他暂时为黑盒,待我们层层拨开。接下来我们就以这个SqlSession为线索,展开源码阅读。
2. SqlSession的创建:我从哪里来?
从官方文档可以理解构建逻辑:
- SqlSessions 是由SqlSessionFactory 实例创建的;
- 而SqlSessionFactory 本身是由 SqlSessionFactoryBuilder创建;
- 而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例;
带着从*“可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory”* 的指示。我们先来了解 Configuration, 阅读官方文档 XML配置 章节,不难得出一个典型的mybatis-config.xml 顶层节点结构如下: 打开org.apache.ibatis.session.Configuration类 试着寻找xml节点的蛛丝马迹, 可以看出Configuration就是xml对应的类。
public class Configuration {
//对应environments(环境配置)
protected Environment environment;
//对应properties(属性)
protected Properties variables = new Properties();
//对应objectFactory(对象工厂)
protected ObjectFactory objectFactory = new DefaultObjectFactory();
//对应typeHandlers(类型对应器)
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
//对应typeAlias(类型别名)
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
//对应mappers(映射器)
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
//对应 plugins(插件)
protected final InterceptorChain interceptorChain = new InterceptorChain();
//...省略
public Configuration() {
//与databaseIdProvider(数据库厂商标识)
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
}
settings(设置)好像没有直接搜索到,通过属性搜索发现settings是散落在 Configuration 中的,不明白MyBatis作者为什么不通过一个Settings 类把setting属性封装起来。现在通通找到xml中节点对应或相似对应。
现在, 可以将SqlSession的创建过程拆解为3个阶段:
- Configuration初始化过程;
- SqlSessionFactory实例的创建过程;
- 创建SqlSessions实例化的过程;
官方示例:
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
简单描述上述代码:
首先,创建了SqlSessionFactoryBuilder对象;
然后, 以Mybatis主配置文件输入流作为参数,调用 SqlSessionFactoryBuilder对象的build方法。
下面是代码实现片段:
public class SqlSessionFactoryBuilder {
//SqlSessionFactory 创建方法
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
// 构建SqlSessionFactory重载方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//创建XMLConfigBuilder
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.
}
}
}
在build()方法中,首先创建一个XMLConfigBuilder对象,然后调用XMLConfigBuilder对象的parse方法对主配置文件进行解析, 生成Configuration对象。以ConfigBuilder对象作为参数调用重载的builder()方法,方法实现片段:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
DefaultSqlSessionFactory类
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
//接收Configuration作为参数的构造方法
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
...//省略
最后,SqlSessionFactory接口的默认实现,即DefaultSqlSessionFactory。通过new 创建了一个DefaultSessionFactory对象。
3.后记
以上, 我们梳理了 MyBatis 的 SqlSession 的创建过程,但是跳过了Configuration的具体构造过程,下一节我们继续来看Configuration的创建。同时来探究Configuration这个类的内部。