MyBatis学习笔记(三)---MyBatis的初始化

前言:通过前面两篇文章,我们对MyBatis有了初步的了解。从这篇开始,我会对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;
}








  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值