博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MyBatis学习笔记(三)---MyBatis的初始化
阅读量:4094 次
发布时间:2019-05-25

本文共 8180 字,大约阅读时间需要 27 分钟。

前言:通过前面两篇文章,我们对MyBatis有了初步的了解。从这篇开始,我会对MyBatis从初始化到执行查询的步骤进行源码的剖析。首先涉及到的就是初始化了。有疑惑可以回到。

一、从代码入手看MyBatis初始化做了什么工作

public class MyBatisUtil {    private static final SqlSessionFactory sqlSessionFactory;    static{        String resource="mybatis-config.xml";        Reader reader=null;        try {            reader=Resources.getResourceAsReader(resource);        } catch (IOException e) {            e.printStackTrace();        }        sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);    }    public static SqlSessionFactory getSqlSessionFactory(){        return sqlSessionFactory;    }}
不难看出,上述代码声明了MyBatis配置文件的名称mybatis-config.xml,使用Resources的静态方法获取配置文件的输入流。有兴趣的话可以追溯一下源码看看。这里重点看一下这一条语句。
sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
点击进去查看SqlSessionFactoryBuilder的build()方法。
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {        SqlSessionFactory var5;        try {            XMLConfigBuilder e = new XMLConfigBuilder(reader, environment, properties);            var5 = this.build(e.parse());        } catch (Exception var14) {            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);        } finally {            ErrorContext.instance().reset();            try {                reader.close();            } catch (IOException var13) {                ;            }        }        return var5;}
先看第一条语句:
XMLConfigBuilder e = new XMLConfigBuilder(reader, environment, properties);

设想我们来设计MyBatis,那么在初始化的时候我们应该做什么呢?显然是解析配置文件和Mapper映射文件(mybatis-config.xml和*mapper.xml)。MyBatis的初始化也正是做了这一步。具体的代码细节涉及到了XML的解析步骤和复杂的类结构,这里只抓住主要思想。

  • XML的解析需要除了需要待解析的XML文件,还需要对应的DTD文件。
  • 配置文件和Mapper文件通过字节流和字符流,进而被封装进了InputSource对象(InputSource是一个封装输入源的对象),最后被组织成了Document的结构化数据。
  • DTD文件同理,在XMLMapperEntityResolver中被组织成了结构化数据。

  • XPathParser组合了Document对象和XMLMapperEntityResolver对象,也即是有了解析XML文件的基础。XPathParser的作用是提供根据XPath表达式获取基本的DOM节点Node信息的操作。

在看第二条语句:

var5 = this.build(e.parse());
这里主要是调用了XMLConfigBuilder的parse()方法。主要的调用链如下:

public Configuration parse() {        if(this.parsed) {            throw new BuilderException("Each XMLConfigBuilder can only be used once.");        } else {            this.parsed = true;            this.parseConfiguration(this.parser.evalNode("/configuration"));            return this.configuration;        }}
private void parseConfiguration(XNode root) {        try {            Properties e = this.settingsAsPropertiess(root.evalNode("settings"));            this.propertiesElement(root.evalNode("properties"));            this.loadCustomVfs(e);            this.typeAliasesElement(root.evalNode("typeAliases"));            this.pluginElement(root.evalNode("plugins"));            this.objectFactoryElement(root.evalNode("objectFactory"));            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));            this.settingsElement(e);            this.environmentsElement(root.evalNode("environments"));            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));            this.typeHandlerElement(root.evalNode("typeHandlers"));            this.mapperElement(root.evalNode("mappers"));        } catch (Exception var3) {            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);        }}
XMLConfigBuilder的parse()方法中有一句this.parser.evalNode("/configuration"),这里其实就是利用了上面讲到的XPathParser对象解析XML文件的<configuration>节点(注意我们传入的文件是mybatis-config.xml配置文件,其根节点正是<configuration>)。解析出的<configuration>节点作为整个DOM的根节点被传给了XMLConfigBuilder的parseConfiguration()方法。依次解析<settings>、<properties>...<mappers>节点。

接下来以别名节点<typeAliases>为例看看这里到底做了什么?

this.typeAliasesElement(root.evalNode("typeAliases"));
private void typeAliasesElement(XNode parent) {        if(parent != null) {            Iterator i$ = parent.getChildren().iterator();            while(i$.hasNext()) {                XNode child = (XNode)i$.next();                String alias;                if("package".equals(child.getName())) {                    alias = child.getStringAttribute("name");                    this.configuration.getTypeAliasRegistry().registerAliases(alias);                } else {                    alias = child.getStringAttribute("alias");                    String type = child.getStringAttribute("type");                    try {                        Class e = Resources.classForName(type);                        if(alias == null) {                            this.typeAliasRegistry.registerAlias(e);                        } else {                            this.typeAliasRegistry.registerAlias(alias, e);                        }                    } catch (ClassNotFoundException var7) {                        throw new BuilderException("Error registering typeAlias for \'" + alias + "\'. Cause: " + var7, var7);                    }                }            }}
可以看到首先对子节点的类型进行区分,对于<typeAlias>子节点,获取其别名<alias>和类型<type>属性的值之后,加载type类,并将别名和类型的映射关系存进了TypeAliasRegistry对象中,它其实是一个Map的映射结构。而从XMLConfigBuilder的父类BaseBuilder的构造器中我们可以看到,其实这个TypeAliasRegistry对象是Configuration对象中结合的对象。也就是说,<typeAliases>节点中的信息最终是被存入了Configuration对象中,其他的<configuration>子节点也是同理的。

public BaseBuilder(Configuration configuration) {        this.configuration = configuration;        this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();}
总之,调用XMLConfigBuilder的parse()方法会把配置文件中的信息全部存入Configuration对象中。
回到刚才的语句中,继续调用SqlSessionFactoryBuilder的build()方法:

var5 = this.build(e.parse());
public SqlSessionFactory build(Configuration config) {        return new DefaultSqlSessionFactory(config);}
这里做的只是将Configuration对象作为参数传递给DefaultSQLSessionFactory的构造器,并将SQLSessionFactory对象返回。

小结:MyBatis的初始化首先是将配置文件、Mapper文件和DTD文件作为输入源分别组织成Document和XMLMapperEntityResolver这种结构化的数据,再封装进XPathParser中,由XPathParser对外部提供节点的解析能力。最终,XML中的各种信息被解析并存储进了一个Configuration对象中,同时返回了一个封装了该Configuration的SQLSessionFactory对象。

二、Configuration对象中存储了哪些信息

上面我们说到MyBatis配置文件中的信息最终被存入了一个Configuration对象。为了后续学习的方便,我们将Configuration对象中存的信息罗列出来。可以参考这段代码:
private void parseConfiguration(XNode root) {        try {            Properties e = this.settingsAsPropertiess(root.evalNode("settings"));            this.propertiesElement(root.evalNode("properties"));            this.loadCustomVfs(e);            this.typeAliasesElement(root.evalNode("typeAliases"));            this.pluginElement(root.evalNode("plugins"));            this.objectFactoryElement(root.evalNode("objectFactory"));            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));            this.settingsElement(e);            this.environmentsElement(root.evalNode("environments"));            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));            this.typeHandlerElement(root.evalNode("typeHandlers"));            this.mapperElement(root.evalNode("mappers"));        } catch (Exception var3) {            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);        } }
  1. settings设置
  2. properties属性
  3. typeAliases类型别名
  4. plugins插件
  5. objectFactory对象工厂
  6. objectWrapperFactory
  7. reflectFactory
  8. databaseProvider
  9. mappers映射器
  10. environments
  • environment
  • transactionManager
  • dataSource

三、涉及到的设计模式

  • Builder模式
记得我们构造SQLSessionFactory的方式,从代码上就可以看出是Builder模式:
new SqlSessionFactoryBuilder().build(reader);
可以看到SQLSessionFactoryBuilder的build()方法表现出了重载的多态,参数个数不定,并且组合的随意度很大。这种时候可以考虑Builder模式以避免代码的冗余。其build方法最终都会调用到同一个方法:

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {        SqlSessionFactory var5;        try {            XMLConfigBuilder e = new XMLConfigBuilder(reader, environment, properties);            var5 = this.build(e.parse());        } catch (Exception var14) {            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);        } finally {            ErrorContext.instance().reset();            try {                reader.close();            } catch (IOException var13) {                ;            }        }        return var5;}

你可能感兴趣的文章
卧槽!小姐姐用动画图解 Git 命令,这也太秀了吧?!
查看>>
厉害了!Python 编辑器界的神器 Jupyter ,推出官方可视化 Debug 工具!
查看>>
卧槽!Java 虚拟机竟然还有这些性能调优技巧...
查看>>
听说玩这些游戏能提升编程能力?
查看>>
7 年工作经验,面试官竟然还让我写算法题???
查看>>
被 Zoom 逼疯的歪果仁,造出了视频会议机器人,同事已笑疯丨开源
查看>>
上古语言从入门到精通:COBOL 教程登上 GitHub 热榜
查看>>
再见,Eclipse...
查看>>
超全汇总!B 站上有哪些值得学习的 AI 课程...
查看>>
如果你还不了解 RTC,那我强烈建议你看看这个!
查看>>
神器面世:让你快速在 iOS 设备上安装 Windows、Linux 等操作系统!
查看>>
沙雕程序员在无聊的时候,都搞出了哪些好玩的小玩意...
查看>>
太赞了!GitHub 标星 2.4k+,《可解释机器学习》中文版正式开放!
查看>>
程序员用 AI 修复百年前的老北京视频后,火了!
查看>>
漫话:为什么你下载小电影的时候进度总是卡在 99% 就不动了?
查看>>
我去!原来大神都是这样玩转「多线程与高并发」的...
查看>>
当你无聊时,可以玩玩 GitHub 上这个开源项目...
查看>>
B 站爆红的数学视频,竟是用这个 Python 开源项目做的!
查看>>
安利 10 个让你爽到爆的 IDEA 必备插件!
查看>>
自学编程的八大误区!克服它!
查看>>