Mybatis源码分析(一):xml解析部分

前言
针对源码总结分为二部分来看,一部分解析环境变量部分和mapper部分,另一部分为执行sql部分。先来看第一部分,xml解析的源码。

例子
一个简单的configuration配置文件和mapper文件,结合这个来看源码
config.xml


    
    
        
            
            
                
                
                
                
            
        
    

    
    
        
        
    

UserMapper.xml




	

入口
XMLConfigBuilder 看这个类的名字我们可以看出来用来解析config配置文件,那我们来看源码部分

/**
   * 解析XML配置文件
   * @return
   */
  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // parser.evalNode("/configuration"):通过XPATH解析器,解析configuration根节点,
    //也就是这个估计大家没有看到过,因为spring结合mybatis封装好了。
    // 从configuration根节点开始解析,最终将解析出的内容封装到Configuration对象中
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

通过parseConfiguration(parser.evalNode("/configuration"))方法来看下一方法

private void parseConfiguration(XNode root) {
    try {
      。。。。
      //这个里面有好多的方法,着重看这两个
      // 根据方法名顾名思义解析 数据源环境配置例如datasour解析就在这部分
      environmentsElement(root.evalNode("environments"));
      // 这个方法用来解析我们mappers标签,每个mappers里面有对应的子mapper的映射
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

这块来分层来看一下
1.数据源解析入口
environmentsElement(root.evalNode(“environments”))

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
      	//结合我们例子xml,如果为空取默认的属性,解析这块可以看个人Dom4j解析xml内容
        environment = context.getStringAttribute("default");
      }
      //获取子标签的属性
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          //事务部分
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //数据源部分,解析出来的数据源放入configuration对象中。这块使用了构建者模式
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          //最终configuration封装了数据源信息
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

数据源文件解析
dataSourceElement(child.evalNode(“dataSource”))

private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      //这块相当于获取property标签并解析 
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      //把Properties 封装到DataSourceFactory ,最终datasource会封装到configuration
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

property标签解析
getChildrenAsProperties()

public Properties getChildrenAsProperties() {
    Properties properties = new Properties();
    for (XNode child : getChildren()) {
      //获取property标签name属性:driver,url信息等
      String name = child.getStringAttribute("name");
      //获取property标签value属性对应的值
      String value = child.getStringAttribute("value");
      if (name != null && value != null) {
      	//获取到以后封装到properties对象中
        properties.setProperty(name, value);
      }
    }
    return properties;
  }

到此块数据源信息就解析完成了。
2.Mapper映射文件入口
mappers文件解析
mapperElement(root.evalNode(“mappers”))

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      // 获取标签的子标签,也就是mapper.xml
      for (XNode child : parent.getChildren()) {
    	// 子标签
        if ("package".equals(child.getName())) {
          // 获取mapper接口和mapper映射文件对应的package包名
          String mapperPackage = child.getStringAttribute("name");
          // 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
          configuration.addMappers(mapperPackage);
        } else {// 子标签
          // 获取resource属性
          String resource = child.getStringAttribute("resource");
          // 获取url属性
          String url = child.getStringAttribute("url");
          // 获取class属性
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 专门用来解析mapper映射文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            // 通过XMLMapperBuilder解析mapper映射文件
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            // 通过XMLMapperBuilder解析mapper映射文件
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class mapperInterface = Resources.classForName(mapperClass);
            // 将指定mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

mapper文件解析
mapperParser.parse()

public void parse() {
	// mapper映射文件是否已经加载过
    if (!configuration.isResourceLoaded(resource)) {
      // 从映射文件中的根标签开始解析,直到完整的解析完毕
      configurationElement(parser.evalNode("/mapper"));
      // 标记已经解析
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

configurationElement(parser.evalNode("/mapper"))
mapper每个标签的属性

private void configurationElement(XNode context) {
    try {
      // 获取namespace值,也就是命名空间
      String namespace = context.getStringAttribute("namespace");
      // 命名空间不能为空
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      
      // 设置当前的命名空间为namespace的值
      builderAssistant.setCurrentNamespace(namespace);
      // 解析子标签
      cacheRefElement(context.evalNode("cache-ref"));
      // 解析子标签
      cacheElement(context.evalNode("cache"));
      
      // 解析子标签
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      // 解析子标签
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      // 解析子标签,也就是SQL片段
      sqlElement(context.evalNodes("/mapper/sql"));
      // 解析\等4个标签的子节点,子节点包括元素节点和文本节点
		NodeList children = node.getNode().getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			XNode child = node.newXNode(children.item(i));
			// 处理文本节点
			if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE
					|| child.getNode().getNodeType() == Node.TEXT_NODE) {
				String data = child.getStringBody("");
				// 将文本内容封装到SqlNode中
				TextSqlNode textSqlNode = new TextSqlNode(data);
				// SQL语句中带有${}的话,就表示是dynamic的
				if (textSqlNode.isDynamic()) {
					contents.add(textSqlNode);
					isDynamic = true;
				} else {
					// SQL语句中(除了${}和下面的动态SQL标签),就表示是static的
					contents.add(new StaticTextSqlNode(data));
				}
				
				//处理元素节点
			} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
				String nodeName = child.getNode().getNodeName();
				// 动态SQL标签处理器
				NodeHandler handler = nodeHandlerMap.get(nodeName);
				if (handler == null) {
					throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
				}
				handler.handleNode(child, contents);
				// 动态SQL标签是dynamic的
				isDynamic = true;
			}
		}
		return new MixedSqlNode(contents);
	}

isDynamic()

public boolean isDynamic() {
    DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
    // 创建分词解析器,主要用来解析SQL字符串的#{}和${}
    GenericTokenParser parser = createParser(checker);
    // 进行解析,如果是${},此处直接解析成完整的SQL语句
    parser.parse(text);
    return checker.isDynamic();
  }

解析${}和#{}
parse()

public String parse(String text) {
    if (text == null || text.isEmpty()) {
      return "";
    }
    // search open token
    int start = text.indexOf(openToken, 0);
    if (start == -1) {
      return text;
    }
    char[] src = text.toCharArray();
    int offset = 0;
    final StringBuilder builder = new StringBuilder();
    StringBuilder expression = null;
    while (start > -1) {
      if (start > 0 && src[start - 1] == '\\') {
        // this open token is escaped. remove the backslash and continue.
        builder.append(src, offset, start - offset - 1).append(openToken);
        offset = start + openToken.length();
      } else {
        // found open token. let's search close token.
        if (expression == null) {
          expression = new StringBuilder();
        } else {
          expression.setLength(0);
        }
        builder.append(src, offset, start - offset);
        offset = start + openToken.length();
        int end = text.indexOf(closeToken, offset);
        while (end > -1) {
          if (end > offset && src[end - 1] == '\\') {
            // this close token is escaped. remove the backslash and continue.
            expression.append(src, offset, end - offset - 1).append(closeToken);
            offset = end + closeToken.length();
            end = text.indexOf(closeToken, offset);
          } else {
            expression.append(src, offset, end - offset);
            offset = end + closeToken.length();
            break;
          }
        }
        if (end == -1) {
          // close token was not found.
          builder.append(src, start, src.length - start);
          offset = src.length;
        } else {
          builder.append(handler.handleToken(expression.toString()));
          offset = end + closeToken.length();
        }
      }
      start = text.indexOf(openToken, offset);
    }
    if (offset < src.length) {
      builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
  }

至此sql语句就解析完了,请求参数和返回参数没有去看,那块根据类型去调用反射生成具体的文件。下一篇看sql执行部分。

你可能感兴趣的:(Mybatis)