Mybatis学习 -- XML文件解析

Mybatis中使用XML文件的地方主要有两个
1、mybatis的配置文件;
2、mybatis的各个mapper文件;


mybatis配置文件解析

    首先来看一下mybatis中的配置文件解析,解析逻辑主要存在于XMLConfigBuilder类中。XMLConfigBuilder类继承了BaseBuilder类,拥有四个私有属性分别是:

    // 该属性主要是保证同一个XMLConfigBuilder只解析一次。
    ​private boolean parsed   
    // 解析XML文件主要的方法,对外提供了evalNode(String expression) 方法,解析之后返回XNode对象。
    ​private final XPathParser parser
    // Reflector类的工厂类,Reflector主要是获取指定Class的元数据信息(get,set方法,属性信息等)。
    ​private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory()
    // 当前环境信息(环境信息有什么用呢?用处是如何体现出来的)
    ​private String environment​​​

    XMLConfigBuilder类解析XML文件的入口方法是parse方法,parse方法中通过parsed字段判断了是否是该类的第一次解析,如果不是第一次解析则直接抛出BuilderException,如果是第一次解析则调用XPathParser类的evalNode(String expression)方法,获取xml文件中“configuration”元素对应的Node。然后调用parseConfiguration(XNode root)方法,解析的属性主要包括(properties,settings,typeAliases,plugins,objectFactory,objectWrapperFactory,reflectorFactory,environments,databaseIdProvider,typeHandlers,mappers)详情可见mybatis官方文档 http://www.mybatis.org/mybatis-3/zh/configuration.html
,解析出来的元素在Configuration对象中都有对应的属性来保存。如下是parse(),parseConfiguration(XNode root)方法。

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

Mapper对应的XML文件解析

    Mapper文件的解析逻辑的入口是从XMLMapperBuilder类的parse方法开始的。XML文件解析主要分为了以下几步:

  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }
  1. 首先判断该资源是否已经加载
    未加载的情况下,会执行以下逻辑
    1. 解析Mapper文件中的所有元素
    2. 将该路径添加到configuration类中的ResourceLoader列表中,防止重复加载
    3. 绑定namespace(命名空间)
  2. 解析ResultMap元素
  3. 解析CacheRefs元素
  4. 解析Statement元素

下面来看一下如何解析**mapper.xml文件中的各个标签,configurationElement(parse.evalNode("/mapper"))方法的实现代码具体如下所示:

  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

    从上面代码中可以看到,主要需要获取的元素包括(cache-ref,cache,/mapper/parameterMap,/mapper/resultMap,/mapper/sql,select|insert|update|delete)获取各个元素的节点信息是通过XNode类提供的evalNode(String)方法来获取的,

  • 解析cache-ref标签,在mybatis中使用二级缓存时,通过cache-ref标签可以指定引用另一个命名空间的缓存。解析cache-ref标签,主要用到的CacheRefResolver和MapperBuilderAssistant类,解析时会根据指定的命名空间,获取该命名空间中的缓存,并指定为当前空间的Cache。
  • 解析cache标签,cache标签可以定义该命名空间下使用的二级缓存,通过type,eviction,flushInterval,size,readOnly,blocking等标签来定制二级缓存的一些特性。在解析cache标签时,会首先解析type,eviction,flushInterval,size,readOnly,blocking这些元素,然后调用MapperBuilderAssistant类的userNewCache方法,创建指定的Cache类,绑定到当前命名空间下。
  • 解析/mapper/parameterMap元素,定义了参数映射关系,mybatis文档中提示,该元素将要废弃了。
  • 解析/mapper/resultMap元素,resultMap定义了返回值与实体类属性之间的映射的关系。逐步解析ResultMap标签下的子标签。最后调用MapperBuilderAssistant类的addResultMap方法,将解析完的ResultMap对象,保存到Configuration类中resultMaps属性中。
  • 解析/mapper/sql元素,mapper文件中的sql标签是用来定义可被其他语句引用的可重复的语句块。解析到的sql标签对应的XNode会保存在XMLMapperBuilder类的sqlFragments属性中。
  • 解析select|insert|update|delete元素,这四个元素对应的就是我们日常操作数据库的增删改查,之前定义的resultMap,cache,parameterMap,sql都有可能在这四个元素解析时用到。解析这四个标签是通过XMLStatementBuilder的parseStatementNode方法来解析,解析出的结果会封装为MapperStatement存入到Configuration类mapperStatments(StrictMap)属性中。

你可能感兴趣的:(Mybatis学习 -- XML文件解析)