Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)

接上篇,继续来看看创建SqlSessionFactory对象的时候都做了些什么

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(1)
上篇文章主要看了一下configuration类中都有些什么东西,这篇文章就从.build()方法开始,一步一步往下看=>

直接从.build()方法进去看:
源码:

 public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

传了一个reader对象和两个null给build(Reader reader, String environment, Properties properties)方法
源码:

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      //使用reader创建一个xml解析器
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      //parser.parse()的作用:将reader中的配置信息进行拆解并各节点数据放入一个configuration对象中
      //即,parser.parse()最终得到一个包含配置文件中所有数据的configuration对象
      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.
      }
    }
  }

try中返回了 build(parser.parser()),finally中关闭reader
先来看看parse对象:

XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);

XMLConfigBuilder:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
再看看它的父类BaseBuilder:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
父类中有三个final的全局变量,configuration已经基本知道是用来存储配置信息的,那再分别看看TypeAliasRegistry和TypeHandlerRegistry
首先来看看TypeAliasRegistry
源码:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
TypeAliasRegistry中创建了一个HashMap的final变量,可以看到构造方法中调用了很多次registerAlias方法,并且这个方法中传入的有Java基本数据类型String和数组等,并将其对应的类传入,那么来看看这个registerAlias方法:

public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
    }
    typeAliases.put(key, value);
  }

一句一句解释的话就是先对alias判空,非空的话将它转为英文小写,再到typeAliases中判断是否存在对应的key,存在的话判断这个key对应的value是否为空,如果不为空的话再判断这个value是不是和传入的value不一致,如果同时满足这三个条件则抛出异常,如果不同时满足的话就将它们放入typeAliases中,,整个的话可以理解为初始化一个map,这个map中存储的是数据类型。

再看看TypeHandlerRegistry:
源码:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
先看一下这几个全局的final变量:
1、jdbcTypeHandlerMap:new的是EnumMap,这个东西我没有用过,但是从字面上来看的话“枚举Map”,嗯,后面还指定了一个JdbcType类,那么先看看这个JdbcType中装了些什么吧:Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
很熟悉的感觉吧,,原来JdbcType是一个枚举类,里面的这些个常量对应的就是数据库中的数据类型嘛,那么知道jdbcTypeHandlerMap这个map中的key就是这些常量了,再看看value是什么
TypeHandler<?>:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)TypeHandler是一个接口,并且有很多的实现类,但是我们看它实现类的名字,都是数据库中的数据类型开头然后接上TypeHandler对吧,那么先暂时根据字面意思理解为数据库类型处理器吧(其实打开之后看了一下,发现看到后面有点看不懂)
好,我们知道jdbcTypeHandlerMap这个map中是以数据库中的数据类型为key存储的对应数据类型的处理器。 后面的几个就先不看了,tmd看是看了一下,描述起来太费力了
那么我们回到BaseBuilder这个类总结一下它的三个final全局变量都是个什么:
第一个:Configuration configuration; 用来存储配置信息的
第二个:TypeAliasRegistry typeAliasRegistry;用来存储数据类型的,其实就是
初始化一个Map<String, Class<?>>
第三个:TypeHandlerRegistry typeHandlerRegistry;用来存储对应数据库中数据类型的处理器

好的,我们到这看完了XMLConfigBuilder的父类BaseBuilder里面的几个final全局变量。。

我们接着看parse对象的创建:

public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    //XMLMapperEntityResolver(mybatis离线dtd文件解析器)、XPathParser对象(转换xml格式数据)
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }

我的注释中写明了XPathParser、XMLMapperEntityResolver都是干嘛的,但是还是跟进去看看吧,先来看XPathParser:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
只是new了一个XMLMapperEntityResolver对象哈,里面也没有构造方法,那么就简单的看看它的变量,可以看到都是final修饰的String类型变量哈,“”中写的都是.dtd文件的名字
可以看到XMLMapperEntityResolver实现了一个接口EntityResolver,也不知道在什么地方通过这个接口来调用这个类中的方法,那么这个类中最重要的方法代码如下:

private InputSource getInputSource(String path, String publicId, String systemId) {
    InputSource source = null;
    if (path != null) {
      try {
        InputStream in = Resources.getResourceAsStream(path);
        source = new InputSource(in);
        source.setPublicId(publicId);
        source.setSystemId(systemId);
      } catch (IOException e) {
        // ignore, null is ok
      }
    }
    return source;
  }

path就是前面定义的dtd文件路径,通过InputSource来加载对应的dtd文件。
好,看完了XMLMapperEntityResolver,那么接着看XPathParser:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
调用了commonConstructor方法,再初始化了一下document这个变量,
先看看commonConstructor方法干了什么:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
简单初始化了一下全局变量。
再看看createDocument(new InputSource(reader)):

private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    //只能被命名为after common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
          // NOP
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

这个就不细看了,总的就是使用xml格式来解析转化成输入流的reader对象,获取reader中的所有节点数据。

好,我们再回到这个地方:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
已经看完了this里面的参数,那么看看this()方法中干了什么:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
看看super()干了什么:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
super()就是初始化了父类BaseBuilder中的几个全局变量。那么总的来说创建parser对象的时候就是以xml格式的方式解析了一下转换成输入流的reader,并且初始化了一下XMLConfigBuilder和BaseBuilder中的全局变量。

好,那么接着看parser.parse()又做了什么吧:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)
重点来了,,,parsed初始化时是false,这是为了保证这个方法只会被使用一次,parseConfiguration(parser.evalNode("/configuration"))用来解析配置文件中configuration标签所包含的所有标签的信息,本质就是根据配置文件来初始化configuration对象,并且这个解析过程只会解析一次

好,看最后的build:

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

根据已经初始化好的configuration的来创建一个DefaultSqlSessionFactory实例,
那么进去瞅瞅看:
Mybatis源码阅读,创建SqlSessionFactory对象都干了什么(2)

public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

创建SqlSessionFactory最终的一步:为DefaultSqlSessionFactory中的configuration进行初始化。

至此,创建SqlSessionFactory对象全过程解读完成。

最后,仅作为本人阅读源码笔记,若有错位还请指正,谢谢

上一篇:Adobe Reader 已停止工作


下一篇:一种基于状态机的 DOM 树生成技术(1)