vlambda博客
学习文章列表

精尽MyBatis源码分析

作者:月圆
来源:cnblogs.com/lifullmoon/p/14015009.html

在MyBatis初始化过程中,大致会有以下几个步骤:

  1. 创建 Configuration全局配置对象,会往 TypeAliasRegistry别名注册中心添加Mybatis需要用到的相关类,并设置默认的语言驱动类为 XMLLanguageDriver
  2. 加载 mybatis-config.xml配置文件、Mapper接口中的注解信息和XML映射文件,解析后的配置信息会形成相应的对象并保存到Configuration全局配置对象中
  3. 构建 DefaultSqlSessionFactory对象,通过它可以创建 DefaultSqlSession对象,MyBatis中 SqlSession的默认实现类

由于在MyBatis的初始化过程中去解析Mapper接口与XML映射文件涉及到的篇幅比较多,XML映射文件的解析过程也比较复杂,所以才分成了后面三个模块,逐步分析,这样便于理解

初始化(一)之加载mybatis-config.xml

本文主要分享的是在MyBatis初始化过程中,是如何加载mybatis-config.xml配置文件的,配置描述请参考:MyBatis官方文档的配置说明 https://mybatis.org/mybatis-3/zh/configuration.html

初始化入口在org.apache.ibatis.session.SqlSessionFactoryBuilder构造器中,因为需要通过mybatis-config.xml配置文件构建一个SqlSessionFactory工厂,用于创建SqlSession会话

主要涉及到以下几个类:

  • org.apache.ibatis.session.SqlSessionFactoryBuilder:用于构建SqlSessionFactory工厂
  • org.apache.ibatis.builder.xml.XMLConfigBuilder:根据配置文件进行解析,开始Mapper接口与XML映射文件的初始化,生成Configuration全局配置对象
  • org.apache.ibatis.builder.xml.XMLMapperBuilder:继承BaseBuilder抽象类,用于解析XML映射文件内的标签
  • org.apache.ibatis.session.Configuration:MyBatis的全局配置对象,保存所有的配置与初始化过程所产生的对象

SqlSessionFactoryBuilder

org.apache.ibatis.session.SqlSessionFactoryBuilder:构建SqlSessionFactory工厂类,里面定义了许多build重载方法,主要分为处理Reader和InputStream两种文件资源对象

我们来看看其中的一个build方法:

public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
/**
* 构造 SqlSessionFactory 对象

* @param reader Reader 对象
* @param environment 环境
* @param properties Properties 变量
* @return SqlSessionFactory 对象
  */
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
/*
  * <1> 创建 XMLConfigBuilder 对象
  * 会生成一个 XPathParser,包含 Document 对象
  * 会创建一个 Configuration 全局配置对象
    */
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
/*
  * <2> 解析 XML 文件并配置到 Configuration 全局配置对象中
  * <3> 创建 DefaultSqlSessionFactory 对象
    */
    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.
    }
    }
    }
}

build方法主要做了三件事:

  1. 创建 XMLConfigBuilder对象,生成 XPathParser配置文件解析器对象和 Configuration全局配置对象
  2. 通过 XMLConfigBuilder对象解析XML映射文件,配置信息、生成的相应对象都会保存至 Configuration全局配置对象中
  3. 构建一个 DefaultSqlSessionFactory对象

XMLConfigBuilder

org.apache.ibatis.builder.xml.XMLConfigBuilder:根据配置文件进行解析,开始Mapper接口与XML映射文件的初始化,生成Configuration全局配置对象

构造方法


public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// <1> 创建 Configuration 对象
super(new Configuration());
// 创建一个当前线程的上下文,记录错误信息
ErrorContext.instance().resource("SQL Mapper Configuration");
// <2> 设置 Configuration 的 variables 属性
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}

  1. 首先会进入 XPathParser的构造方法,将XML配置文件解析成 org.w3c.dom.Document对象,这里传入了 XMLMapperEntityResolver作为解析实例对象,其中使用到MyBatis本地的DTD文件
  2. 然后进入 XMLConfigBuilder的另一个构造方法,会先创建一个Configuration全局配置对象,初始化一些对象

parse方法

public Configuration parse() {
    // <1.1> 若已解析,抛出 BuilderException 异常
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    // <1.2> 标记已解析
    parsed = true;
    // <2> 解析 XML configuration 节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}
private void parseConfiguration(XNode root) {
    try {
        // issue #117 read properties first
        // <1> 解析 <properties /> 标签
        propertiesElement(root.evalNode("properties"));
        // <2> 解析 <settings /> 标签,解析配置生成 Properties 对象
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        // 根据配置加载自定义 VFS 实现类
        loadCustomVfs(settings);
        // 根据配置加载自定义的 Log 实现类
        loadCustomLogImpl(settings);
        // <3> 解析 <typeAliases /> 标签,生成别名与类的映射关系
        typeAliasesElement(root.evalNode("typeAliases"));
        // <4> 解析 <plugins /> 标签,添加自定义拦截器插件
        pluginElement(root.evalNode("plugins"));
        // <5> 解析 <objectFactory /> 标签,自定义实例工厂
        objectFactoryElement(root.evalNode("objectFactory"));
        // <6> 解析 <objectWrapperFactory /> 标签,自定义 ObjectWrapperFactory 工厂,无默认实现
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        // <7> 解析 <reflectorFactory /> 标签,自定义 Reflector 工厂
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        // 将 <settings /> 配置信息添加到 Configuration 属性
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        // <8> 解析 <environments /> 标签,自定义当前环境信息
        environmentsElement(root.evalNode("environments"));
        // <9> 解析 <databaseIdProvider /> 标签,数据库标识符
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        // <10> 解析 <typeHandlers /> 标签,自定义类型处理器
        typeHandlerElement(root.evalNode("typeHandlers"));
        // <11> 解析 <mappers /> 标签,扫描Mapper接口并进行解析
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

parse()解析方法中,获取到Document对象的<configuration />节点,然后调用parseConfiguration进行解析,依次解析以下标签:

<1>解析<properties />标签,调用propertiesElement方法

<2>解析<settings />标签,解析配置生成 Properties 对象,调用settingsAsProperties方法

<3>解析<typeAliases />标签,生成别名与类的映射关系,调用typeAliasesElement方法

<4>解析<plugins />标签,添加自定义拦截器插件,调用pluginElement方法

<5>解析<objectFactory />标签,自定义实例工厂,调用objectFactoryElement方法

<6>解析<objectWrapperFactory />标签,自定义 ObjectWrapperFactory 工厂,调用objectWrapperFactoryElement方法

<7>解析<reflectorFactory />标签,自定义 Reflector 工厂,调用reflectorFactoryElement方法

<8>解析<environments />标签,自定义当前环境信息,调用environmentsElement方法

<9>解析<databaseIdProvider />标签,数据库标识符,调用databaseIdProviderElement方法

<10>解析<typeHandlers />标签,自定义类型处理器,调用typeHandlerElement方法

<11>解析<mappers />标签,扫描Mapper接口并进行解析,调用mapperElement方法

propertiesElement方法

  1. 解析 <properties /> 标签,成 Properties 对象。
  2. 覆盖 configuration 中的 Properties 对象到上面的结果。
  3. 设置结果到 parserconfiguration 中。

settingsAsProperties方法

  1. <setting /> 标签解析为 Properties 对象
  2. 接下来会根据该属性对象加载用户自定义的VFS实现类和Log实现类,并将配置信息配置到Configuration全局配置对象中

typeAliasesElement方法

  1. <typeAliases /> 的子标签进行解析
  2. 如果是 <package />字标签,则对其配置的name包名进行解析,将别名去Class对象进行映射往TypeAliasRegistry注册,调用 TypeAliasRegistry.registerAliases方法
  3. 否则就是配置的单个别名配置,进行解析并添加到TypeAliasRegistry中

pluginElement方法

  1. <plugins /> 的子标签进行解析
  2. 生成对应的Interceptor拦截器对象并设置配置的属性
  3. 将拦截器添加到 Configuration 全局配置的 InterceptorChain 拦截器调用链中,后续会讲到,例如 com.github.pagehelper.PageInterceptor分页插件

总结

MyBatis会在SqlSessionFactoryBuilder构造器中根据mybatis-config.xml配置文件初始化Configuration全局配置对象,然后创建对应的DefaultSqlSessionFactory工厂

在解析配置文件的过程中,MyBatis的配置都会保存在Configuration对象中,对Mapper接口和XML映射文件进行解析生成相应的对象都会保存在其中

-- END --

精尽MyBatis源码分析 | 更多精彩文章 -





《Java进阶知识手册》

精尽MyBatis源码分析

精尽MyBatis源码分析

 
   
   
 
▽想知道更多?

长按/扫码关注我吧↑↑↑

觉得不错就点个在看