对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外。本章将通过以下几点详细介绍MyBatis的初始化过程。

  1. MyBatis的初始化做了什么

  2. MyBatis基于XML配置文件创建Configuration对象的过程

  3. 手动加载XML配置文件创建Configuration对象完成初始化,创建并使用SqlSessionFactory对象

  4. 涉及到的设计模式

一、 MyBatis的初始化做了什么

任何框架的初始化,无非是加载自己运行时所需要的配置信息。MyBatis的配置信息,大概包含以下信息,其高层级结构如下:

× configuration 配置

× properties 属性
        × settings 设置
        × typeAliases 类型命名
        × typeHandlers 类型处理器
        × objectFactory 对象工厂
        × plugins 插件
        × environments 环境
        × environment 环境变量
        × transactionManager 事务管理器
        × dataSource 数据源

×映射器

MyBatis的上述配置信息会配置在XML配置文件中,那么,这些信息被加载进入MyBatis内部,MyBatis是怎样维护的呢?

MyBatis采用了一个非常直白和简单的方式---使用 org.apache.ibatis.session.Configuration 对象作为一个所有配置信息的容器,Configuration对象的组织结构和XML配置文件的组织结构几乎完全一样(当然,Configuration对象的功能并不限于此,它还负责创建一些MyBatis内部使用的对象,如Executor等,这将在后续的文章中讨论)。如下图所示:

MyBatis根据初始化好Configuration信息,这时候用户就可以使用MyBatis进行数据库操作了。

可以这么说,MyBatis初始化的过程,就是创建 Configuration对象的过程。

MyBatis的初始化可以有两种方式:

  • 基于XML配置文件:基于XML配置文件的方式是将MyBatis的所有配置信息放在XML文件中,MyBatis通过加载并XML配置文件,将配置文信息组装成内部的Configuration对象

  • 基于Java API:这种方式不使用XML配置文件,需要MyBatis使用者在Java代码中,手动创建Configuration对象,然后将配置参数set 进入Configuration对象中

(PS:  MyBatis具体配置信息有哪些,又分别表示什么意思,不在本文的叙述范围,读者可以参考我的《Java Persistence withMyBatis 3 (中文版)》 的第二章 引导MyBatis中有详细的描述)

接下来我们将通过 基于XML配置文件方式的MyBatis初始化,深入探讨MyBatis是如何通过配置文件构建Configuration对象,并使用它的。

二、MyBatis基于XML配置文件创建Configuration对象的过程

现在就从使用MyBatis的简单例子入手,深入分析一下MyBatis是怎样完成初始化的,都初始化了什么。看以下代码:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List list = sqlSession.selectList("com.foo.bean.BlogMapper.queryAllBlogInfo");

有过MyBatis使用经验的读者会知道,上述语句的作用是执行com.foo.bean.BlogMapper.queryAllBlogInfo 定义的SQL语句,返回一个List结果集。总的来说,上述代码经历了mybatis初始化 -->创建SqlSession -->执行SQL语句 返回结果三个过程。

上述代码的功能是根据配置文件mybatis-config.xml  配置文件,创建SqlSessionFactory对象,然后产生SqlSession,执行SQL语句。而mybatis的初始化就发生在第三句:SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  现在就让我们看看第三句到底发生了什么。

MyBatis初始化基本过程:

SqlSessionFactoryBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例。

初始化的基本过程如下序列图所示:

由上图所示,mybatis初始化要经过简单的以下几步:

1. 调用SqlSessionFactoryBuilder对象的build(inputStream)方法;

2. SqlSessionFactoryBuilder会根据输入流inputStream等信息创建XMLConfigBuilder对象;

3. SqlSessionFactoryBuilder调用XMLConfigBuilder对象的parse()方法;

4. XMLConfigBuilder对象返回Configuration对象;

5. SqlSessionFactoryBuilder根据Configuration对象创建一个DefaultSessionFactory对象;

6. SqlSessionFactoryBuilder返回 DefaultSessionFactory对象给Client,供Client使用。

SqlSessionFactoryBuilder相关的代码如下所示:

public SqlSessionFactory build(InputStream inputStream)
{return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
{try{//2. 创建XMLConfigBuilder对象用来解析XML配置文件,生成Configuration对象XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);//3. 将XML配置文件内的信息解析成Java对象Configuration对象Configuration config = parser.parse();//4. 根据Configuration对象创建出SqlSessionFactory对象return build(config);}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.}}
}
//从此处可以看出,MyBatis内部通过Configuration对象来创建SqlSessionFactory,用户也可以自己通过API构造好Configuration对象,调用此方法创建SqlSessionFactory
public SqlSessionFactory build(Configuration config)
{return new DefaultSqlSessionFactory(config);
}

上述的初始化过程中,涉及到了以下几个对象:

  • SqlSessionFactoryBuilder : SqlSessionFactory的构造器,用于创建SqlSessionFactory,采用了Builder设计模式

  • Configuration :该对象是mybatis-config.xml文件中所有mybatis配置信息

  • SqlSessionFactory:SqlSession工厂类,以工厂形式创建SqlSession对象,采用了Factory工厂设计模式

  • XmlConfigParser :负责将mybatis-config.xml配置文件解析成Configuration对象,共SqlSessonFactoryBuilder使用,创建SqlSessionFactory

创建Configuration对象的过程

接着上述的 MyBatis初始化基本过程讨论,当SqlSessionFactoryBuilder执行build()方法,调用了XMLConfigBuilder的parse()方法,然后返回了Configuration对象。那么parse()方法是如何处理XML文件,生成Configuration对象的呢?

1. XMLConfigBuilder会将XML配置文件的信息转换为Document对象,而XML配置定义文件DTD转换成XMLMapperEntityResolver对象,然后将二者封装到XpathParser对象中,XpathParser的作用是提供根据Xpath表达式获取基本的DOM节点Node信息的操作。如下图所示:

2.  之后XMLConfigBuilder调用parse()方法:会从XPathParser中取出 <configuration>节点对应的Node对象,然后解析此Node节点的子Node:properties, settings, typeAliases,typeHandlers, objectFactory, objectWrapperFactory, plugins, environments,databaseIdProvider, mappers

public Configuration parse(){if (parsed){throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;//源码中没有这一句,只有 parseConfiguration(parser.evalNode("/configuration"));//为了让读者看得更明晰,源码拆分为以下两句XNode configurationNode = parser.evalNode("/configuration");parseConfiguration(configurationNode);return configuration;}/*解析 "/configuration"节点下的子节点信息,然后将解析的结果设置到Configuration对象中*/private void parseConfiguration(XNode root) {try {//1.首先处理properties 节点  propertiesElement(root.evalNode("properties")); //issue #117 read properties first//2.处理typeAliasestypeAliasesElement(root.evalNode("typeAliases"));//3.处理插件pluginElement(root.evalNode("plugins"));//4.处理objectFactoryobjectFactoryElement(root.evalNode("objectFactory"));//5.objectWrapperFactoryobjectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));//6.settingssettingsElement(root.evalNode("settings"));//7.处理environmentsenvironmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631//8.databasedatabaseIdProviderElement(root.evalNode("databaseIdProvider"));//9. typeHandlerstypeHandlerElement(root.evalNode("typeHandlers"));//10 mappersmapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}

注意:在上述代码中,还有一个非常重要的地方,就是解析XML配置文件子节点<mappers>的方法mapperElements(root.evalNode("mappers")), 它将解析我们配置的Mapper.xml配置文件,Mapper配置文件可以说是MyBatis的核心,MyBatis的特性和理念都体现在此Mapper的配置和设计上,我们将在后续的文章中讨论它,敬请期待~

3.  然后将这些值解析出来设置到Configuration对象中。

解析子节点的过程这里就不一一介绍了,用户可以参照MyBatis源码仔细揣摩,我们就看上述的environmentsElement(root.evalNode("environments")); 方法是如何将environments的信息解析出来,设置到Configuration对象中的:

/*解析environments节点,并将结果设置到Configuration对象中注意:创建envronment时,如果SqlSessionFactoryBuilder指定了特定的环境(即数据源);则返回指定环境(数据源)的Environment对象,否则返回默认的Environment对象;这种方式实现了MyBatis可以连接多数据源
*/
private void environmentsElement(XNode context) throws Exception
{if (context != null){if (environment == null){environment = context.getStringAttribute("default");}for (XNode child : context.getChildren()){String id = child.getStringAttribute("id");if (isSpecifiedEnvironment(id)){//1.创建事务工厂 TransactionFactoryTransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//2.创建数据源DataSourceDataSource dataSource = dsFactory.getDataSource();//3. 构造Environment对象Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);//4. 将创建的Envronment对象设置到configuration 对象中configuration.setEnvironment(environmentBuilder.build());}}}
}
private boolean isSpecifiedEnvironment(String id)
{if (environment == null){throw new BuilderException("No environment specified.");}else if (id == null){throw new BuilderException("Environment requires an id attribute.");}else if (environment.equals(id)){return true;}return false;
}

4.  返回Configuration对象

我们将上述的MyBatis初始化基本过程的序列图细化。

三、手动加载XML配置文件创建Configuration对象完成初始化,创建并使用SqlSessionFactory对象

我们可以使用XMLConfigBuilder手动解析XML配置文件来创建Configuration对象,代码如下:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//手动创建XMLConfigBuilder,并解析创建Configuration对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null,null);
Configuration configuration=parse();
//使用Configuration对象创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
//使用MyBatis
SqlSession sqlSession = sqlSessionFactory.openSession();
List list = sqlSession.selectList("com.foo.bean.BlogMapper.queryAllBlogInfo");

四、涉及到的设计模式

初始化的过程涉及到创建各种对象,所以会使用一些创建型的设计模式。在初始化的过程中,Builder模式运用的比较多。

Builder模式应用1: SqlSessionFactory的创建

对于创建SqlSessionFactory时,会根据情况提供不同的参数,其参数组合可以有以下几种:

由于构造时参数不定,可以为其创建一个构造器Builder,将SqlSessionFactory的构建过程和表示分开:

MyBatis将SqlSessionFactoryBuilder和SqlSessionFactory相互独立。

Builder模式应用2: 数据库连接环境Environment对象的创建

在构建Configuration对象的过程中,XMLConfigParser解析 mybatis XML配置文件节点<environment>节点时,会有以下相应的代码:

private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {environment = context.getStringAttribute("default");}for (XNode child : context.getChildren()) {String id = child.getStringAttribute("id");//是和默认的环境相同时,解析之if (isSpecifiedEnvironment(id)) {TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));DataSource dataSource = dsFactory.getDataSource();//使用了Environment内置的构造器Builder,传递id 事务工厂和数据源Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}}

在Environment内部,定义了静态内部Builder类:

public final class Environment {private final String id;private final TransactionFactory transactionFactory;private final DataSource dataSource;public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {if (id == null) {throw new IllegalArgumentException("Parameter 'id' must not be null");}if (transactionFactory == null) {throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");}this.id = id;if (dataSource == null) {throw new IllegalArgumentException("Parameter 'dataSource' must not be null");}this.transactionFactory = transactionFactory;this.dataSource = dataSource;}public static class Builder {private String id;private TransactionFactory transactionFactory;private DataSource dataSource;public Builder(String id) {this.id = id;}public Builder transactionFactory(TransactionFactory transactionFactory) {this.transactionFactory = transactionFactory;return this;}public Builder dataSource(DataSource dataSource) {this.dataSource = dataSource;return this;}public String id() {return this.id;}public Environment build() {return new Environment(this.id, this.transactionFactory, this.dataSource);}}public String getId() {return this.id;}public TransactionFactory getTransactionFactory() {return this.transactionFactory;}public DataSource getDataSource() {return this.dataSource;}}

以上就是本文的全部内容,希望对大家有所帮助!上述内容如有不妥之处,还请读者指出,共同探讨,共同进步!

当面试官问我Mybatis初始化原理时,我笑了相关推荐

  1. inputstream 初始化_如何完美回答面试官问的Mybatis初始化原理!

    前言 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程. MyBatis的初始化做了什么 MyBatis基于XML配置文件 ...

  2. java执行sql文件_面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他

    初识 MyBatis MyBatis 是第一个支持自定义 SQL.存储过程和高级映射的类持久框架.MyBatis 消除了大部分 JDBC 的样板代码.手动设置参数以及检索结果.MyBatis 能够支持 ...

  3. 【255期】面试官问:MyBatis 二级缓存,如何实现关联刷新功能?

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每一天进步一点点,是成功的开始... 1.MyB ...

  4. js var是什么类型_面试官问你JS基本类型时他想知道什么?

    点击上方"IT平头哥联盟",选择"置顶或者星标" 一起进步- 面试的时候我们经常会被问答js的数据类型.大部分情况我们会这样回答包括: 1.基本类型(值类型或者 ...

  5. 【264期】面试官问:Spring Boot 启动时自动执行代码方式有哪几种?解释一二!...

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每一天进步一点点,是成功的开始... 前言 目前 ...

  6. obj: object是什么意思_面试官问你JavaScript基本类型时他想知道什么?

    本文原载于SegmentFault专栏"前端小将" 整理编辑:SegmentFault 面试的时候我们经常会被问答js的数据类型.大部分情况我们会这样回答包括: 1.基本类型(值类 ...

  7. 面试官问你MyBatis中有哪些设计模式,把这篇文章发给他

    戳蓝字"CSDN云计算"关注我们哦! 作者 | 疯狂的蚂蚁 来源 | https://dwz.cn/KFgol1De 之前总结过一篇Spring中用到了哪些设计模式:<面试官 ...

  8. 面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他

    来自:Java建设者 初识 MyBatis MyBatis 是第一个支持自定义 SQL.存储过程和高级映射的类持久框架.MyBatis 消除了大部分 JDBC 的样板代码.手动设置参数以及检索结果.M ...

  9. 阿里面试官问我Mybatis,我怂了吗?

    文章目录 前言 Mybatis入门 1. 什么是MyBatis 2. 为什么我们要用Mybatis? 3. Mybatis快速入门 3.1 导入开发包 3.2 准备测试工作 3.3 创建mybatis ...

最新文章

  1. Android JetPack Lifecycle源码解析
  2. excel操作技巧记录(实时更新)
  3. RabbitMq+Haproxy负载均衡
  4. linux自动获取ip网卡配置文件,linux 命令行下配置网卡自动获取 IP
  5. 建立索引lucene_用Lucene建立搜索索引
  6. JSP内置对象之WEB安全性及config对象
  7. 【APICloud系列|6】使用APICloud接入客服系统美洽获取Appkey配置
  8. 基本数据类型的成员变量放在jvm的哪块内存区域里?
  9. nyoj 聪明的kk
  10. 解决springboot工程创建是连接失败问题
  11. 世上最齐全黑链(隐藏链接)代码大全
  12. svn往分支提代码_[转]代码管理|svn分支管理
  13. TOUCH  PANEL
  14. 队列的介绍和实现(C语言实现)
  15. 转:[IDEA]Java:“程序包XXX不存在”问题的三种解决方案
  16. 一键adb关闭系统更新 坚果手机_华为手机通过ADB永久关闭系统更新
  17. MWC就快到来!5G、物联网应用以及折迭手机为本次焦点
  18. 斯嘉丽约翰逊60张pdf什么时间的?_什么叫美丽与演技并存?
  19. 阿里云服务器后台运行服务
  20. win10解决,你没有权限打开该文件,请向文件的所有者或管理员申请权限

热门文章

  1. 高斯分布绘图的一些记录
  2. 【 Thread】创建线程的2种方法
  3. requests基础3
  4. MariaDB Spider:实现MySQL横纵向扩展的小能手
  5. arcgis api for flex 开发入门(五)查询
  6. 为你的移动页面寻找一丝新意——手机互动网页项目总结(上)
  7. Android开发8:UI组件TextView,EditText,Button
  8. 在使用RegularExpressionValidator验证控件时的验证功能及其验证表达式
  9. FTP匿名用户的配置
  10. 【OpenCV】使用projectPoints实现透视图到俯视图的变化效果