MyBatis源码剖析(3)XMLConfigBuilder(1)

326 阅读4分钟

上一篇文章谈到了在SqlSessionFactoryBuilder中通过XMLConfigBuilder中的parse方法进行配置文件的解析,返回Configuration,这节就谈一谈XMLConfigBuilder

构造函数

在其中共有七个构造函数,还是分为了两个流派,Reader派和InputStream派,然后通过构造函数的重载,最终只流向了一个构造函数。

/**
 * 传入的流形式无论为Reader还是InputStream都将转换为XPathParser
 *
 * @param parser
 * @param environment
 * @param props
 */
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  super(new Configuration());

  // 日志相关
  ErrorContext.instance().resource("SQL Mapper Configuration");
  this.configuration.setVariables(props);

  // 将解析标志位置为 false
  this.parsed = false;
  this.environment = environment;

  // 设置解析器
  this.parser = parser;
}

关于构造函数的方法:

  • parser
  • xpath解析器(可以参考xpath在java中的用法,这里就不介绍了)
  • environment和props在上一篇SqlSessionFactoryBuilder介绍过了

执行流程

  1. 首先调用父类的构造函数,通过new Configuration(),在父类BaseBuilder中进行一些Configuration的初始化工作(其中的细节会在分析BaseBuilder的文章中进行展开),为返回的Configuration做准备
  2. 执行日志的记录
  3. 将传入的props放入到待返回的Configuration中(这里注意,传入的props可能为null)
  4. 为解析标志parsed设置初始值(parsed属性的作用在下面会进行分析)
  5. 初始化默认连接环境
  6. 设置xpath解析器

属性

/** 标志是否解析过 false为没有进行解析过,true代表已经解析过 */
private boolean parsed;

/** xpath解析器 */
private final XPathParser parser;

/** environment 连接的信息 */
private String environment;

/** 在解析 properties 时使用 */
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();

其中要特别说明的一点就是parsed字段,这个字段用来标记xml是否被解析过了,同一个XMLConfigBuilder只允许对配置文件解析一次,解析过后parsed将被置为true

入口方法parse()分析

/**
 * 解析入口函数
 *
 * @return {@link Configuration} 配置信息
 */
public Configuration parse() {
  if (parsed) {
    // 只能进行一次解析,进行第二次解析将会抛出异常
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }

  parsed = true;

  // xpath解析所有节点
  parseConfiguration(parser.evalNode("/configuration"));

  return configuration;
}

在parse方法的初始执行位置就可以看到,会先对parsed进行判断,如果之前解析过了配置文件就会抛出异常,之前没有进行解析过会在解析之后将parsed置为true。

进入到parseConfiguration方法中。

/**
 * 汇总解析,其中又包含了解析每种节点
 *
 * @param root {@link XNode}
 */
private void parseConfiguration(XNode root) {
  try {
    // 解析properties
    propertiesElement(root.evalNode("properties"));

    // 解析settings
    Properties settings = settingsAsProperties(root.evalNode("settings"));

    // 加载自定义文件虚拟系统
    loadCustomVfs(settings);
    loadCustomLogImpl(settings);

    // 解析类、包别名
    typeAliasesElement(root.evalNode("typeAliases"));

    // 解析plugins插件(自定义拦截器)
    pluginElement(root.evalNode("plugins"));

    // 创建对象的工厂,默认为DefaultObjectFactory
    objectFactoryElement(root.evalNode("objectFactory"));

    // 对象包装工厂
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

    // 加载reflectorFactory
    reflectorFactoryElement(root.evalNode("reflectorFactory"));

    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631

    // 配置environments
    environmentsElement(root.evalNode("environments"));

    // 配置databaseIdProvider
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));

    // 配置typeHandlers
    typeHandlerElement(root.evalNode("typeHandlers"));

    // 配置mappers
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

这个里面包含了好多个具体的解析,这就是在分块解析xml文件,每个方法解析对应的xml中的标签

每个解析方法的细节会在下一篇文章中进行详细分析

执行完parseConfiguration方法后,所有的配置信息都已经解析完毕,接下来就会返回Configuration,很多人会纳闷,这个Configuration是在哪定义的,偷偷的告诉你,Configuration定义在XMLConfigBuilder父类BaseBuilder中(这样做的作用和意义都会在分析BaseBuilder源码的文章中讲解)