美文网首页
Mybatis学习 -- XML文件解析

Mybatis学习 -- XML文件解析

作者: tom_xin | 来源:发表于2019-05-21 08:13 被阅读0次

    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<String,MappedStatement>)属性中。

    相关文章

      网友评论

          本文标题:Mybatis学习 -- XML文件解析

          本文链接:https://www.haomeiwen.com/subject/twiboqtx.html