Bootstrap

MyBatis 3 解析mybatis-config.xml配置

按照惯例,直接上干货。

MyBatis初始化工作包括加载和解析mybatis-config.xml配置文件、映射文件和相关注解信息。初始化入口是SqlSessionFactoryBuider.build()方法。

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

XMLConfigBuilder对象继承BaseBuider抽象类,用来解析mybatis-config.xml配置文件。而BaseBuider类中主要包括3个配置项:

public abstract class BaseBuilder {

  protected final Configuration configuration;  //全局唯一配置对象
  protected final TypeAliasRegistry typeAliasRegistry;  //解析mybatis-config.xml中标签
  protected final TypeHandlerRegistry typeHandlerRegistry;  //解析mybatis-config.xml中标签
  
}

其中TypeAliasRegistry和TypeHandlerRegistry在Configuration初始化时配创建出来,也是全局唯一配置:

public class Configuration {
  
	protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

}

当然,Configuration类中并不是只有以上2项配置,实际上MyBatis的配置都存放在这里,这里就不一一展开说了。

MyBatis的初始化是在XMLConfigBuilder类中完成的,由其负责解析mybatis-config.xml配置文件,其核心属性包括:

public class XMLConfigBuilder extends BaseBuilder {

  //是否已经解析过mybatis-config.xml文件
  private boolean parsed;
  //用来解析mybatis-config.xml文件的对象
  private final XPathParser parser;
  //标记标记的名称,默认读取default属性
  private String environment;
  //用来创建和缓存Reflector对象
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
  
}

解析方法:

  //解析配置
  public Configuration parse() {
    //只解析加载一次
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //根节点是configuration
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

//解析配置中各个节点
  private void parseConfiguration(XNode root) {
    try {
      //1.解析节点
      propertiesElement(root.evalNode("properties"));
      //2.解析节点
      typeAliasesElement(root.evalNode("typeAliases"));
      //3.解析节点
      pluginElement(root.evalNode("plugins"));
      //4.解析节点
      objectFactoryElement(root.evalNode("objectFactory"));
      //5.解析节点
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //6.解析节点
      settingsElement(root.evalNode("settings"));
      //7.解析节点
      environmentsElement(root.evalNode("environments"));
      //8.解析节点
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //9.解析节点
      typeHandlerElement(root.evalNode("typeHandlers"));
      //10.解析节点
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

对应官方给出的配置项:

Mybatis配置各属性含义及赋值就不在这里赘述了。以解析节点为例,XMLConfigBuilder.propertiesElement()方法会解析节点并转化成Properties对象保存到XPathParser和Configuration的variables字段中。具体实现如下:

private void propertiesElement(XNode context) throws Exception {
  //
  //    
  //    
  //
    if (context != null) {
      //如果在这些地方,属性多于一个的话,MyBatis 按照如下的顺序加载它们:

      //1.读取property属性。
      //2.读取url或resource对应的属性,第19行指明二者不能同时配置。
      //3.作为方法参数传递的属性最后被读取
      //如果属性名称相同,后加载的值会覆盖前者

      //1.XNode.getChildrenAsProperties函数方便得到孩子所有Properties
      Properties defaults = context.getChildrenAsProperties();
      //2.然后查找resource或者url,加入前面的Properties
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //3.Variables也全部加入Properties
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

其他大多数配置的加载过程跟上面过程相似,也比较好理解,鉴于篇幅原因建议大家看看源码,后面文章还会讲一下解析节点,因为MyBatis初始化过程中还需要加载全部的映射配置。待续...