mytatis解析xml的过程01

来源:互联网 发布:淘宝客api接口 教程 编辑:程序博客网 时间:2024/06/05 00:39

###描述一下mybatis是怎么解析xml的?

1、Xml配置构建器,和它的parse方法

SqlSessionFactoryBuilder类中有build的具体代码。  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {    try {      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);      return build(parser.parse());
我们可以看到XMLConfigBuilder 对象。XMLConfigBuilder 这个对象获取了build方法传入的流,构造了一个XML配置文件解析器。并且在下一行调用了解析器的parse方法。让我们来猜一猜,这个解析器会做什么事情呢?它肯定是1)将xml封装成了dom对象,然后获取了xmlDOM中的各个属性,将其封装到了Configuration 中,别问我为什么,因为build的参数是Configuration。

2、parse方法里做了什么?

那么现在让我们来证实一下。

  private void parseConfiguration(XNode root) {    try {      propertiesElement(root.evalNode("properties")); //issue #117 read properties first      typeAliasesElement(root.evalNode("typeAliases"));      pluginElement(root.evalNode("plugins"));      objectFactoryElement(root.evalNode("objectFactory"));      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));      settingsElement(root.evalNode("settings"));      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631      databaseIdProviderElement(root.evalNode("databaseIdProvider"));      typeHandlerElement(root.evalNode("typeHandlers"));      mapperElement(root.evalNode("mappers"));

我们在XMLConfigBuilder们类中发现上面的代码被调用了。查看h核心配置文件的dtd约束文件可以发现,evalNode方法中的参数字符串,都是核心配置文件中可以被定义的元素。你可以去印证一下。

3、Mappers-mapperElement方法

我们发现了mappers这个字眼。它和我们配置在核心配置中的,指示mapper.xml文件位置的标签名是一样的。那mapperElement方法里面到底是什么呢?


一下是点进方法mapperElement方法里获取到的代码。
  private void mapperElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        if ("package".equals(child.getName())) {          String mapperPackage = child.getStringAttribute("name");          configuration.addMappers(mapperPackage);        } else {          String resource = child.getStringAttribute("resource");          String url = child.getStringAttribute("url");          String mapperClass = child.getStringAttribute("class");          if (resource != null && url == null && mapperClass == null) {            ErrorContext.instance().resource(resource);            InputStream inputStream = Resources.getResourceAsStream(resource);            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());            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());            mapperParser.parse();          } else if (resource == null && url == null && mapperClass != null) {            Class<?> mapperInterface = Resources.classForName(mapperClass);            configuration.addMapper(mapperInterface);          } else {            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");          }        }      }
可以发现,for循环遍历了mappers的子节点。当然,mappers的子节点可能是下面这样的
<mapper resource="com/iktz/mybatis/beans/UserMapper.xml" />
当然也可能更丰满
String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");
这3行代码,说明了mapper标签可以有这3个属性。

  if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);InputStream inputStream = Resources.getResourceAsStream(resource);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());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());mapperParser.parse();  } else if (resource == null && url == null && mapperClass != null) {Class<?> mapperInterface = Resources.classForName(mapperClass);configuration.addMapper(mapperInterface);  } else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");  }
而上面的代码则说明,这3个属性,同时只能存在一个。

我们来看看他们的区别吧!

4、XMLMapperBuilder 类和它的parse方法

  if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);InputStream inputStream = Resources.getResourceAsStream(resource);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());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());mapperParser.parse();  }
他们最让我好奇的是parse方法。我非常想知道他们里面是什么。
  public void parse() {    if (!configuration.isResourceLoaded(resource)) {      configurationElement(parser.evalNode("/mapper"));      configuration.addLoadedResource(resource);      bindMapperForNamespace();    }    parsePendingResultMaps();    parsePendingChacheRefs();    parsePendingStatements();  }
Pend、、这个单词的意思是预备的意思。ChacheRef这个是什么鬼?

5、解析mapper.xml的关键部分

你肯定知道,我要看最后的3行代码。但是其实不是,真正关键的代码是这一行
  configurationElement(parser.evalNode("/mapper"));

configurationElement方法中的代码才是对mapper文件的解析。
private void configurationElement(XNode context) {try {  String namespace = context.getStringAttribute("namespace");  if (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"));

我突然有一种感觉,这里的每一句话都很关键。
那就让我们一句一句来读吧
cacheRefElement(context.evalNode("cache-ref"));
这句应该是关于缓存的,你看方法名上都带有cache字眼。整个方法名仿佛在说,这个方法里要做的事情是缓存指向的元素。


算了,这里先不看了,给下次留点期待。而不能留给下次的,是这3个问题:parameterMapElement,resultMapElements,buildStatementFromContext。

6、解析Parameter,获取ParameterMapping

解析Parameter是由上面提到的parameterMapElement方法完成的。
以下是这个方法的代码。
  private void parameterMapElement(List<XNode> list) throws Exception {    for (XNode parameterMapNode : list) {      String id = parameterMapNode.getStringAttribute("id");      String type = parameterMapNode.getStringAttribute("type");      Class<?> parameterClass = resolveClass(type);      List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");      List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();      for (XNode parameterNode : parameterNodes) {        String property = parameterNode.getStringAttribute("property");        String javaType = parameterNode.getStringAttribute("javaType");        String jdbcType = parameterNode.getStringAttribute("jdbcType");        String resultMap = parameterNode.getStringAttribute("resultMap");        String mode = parameterNode.getStringAttribute("mode");        String typeHandler = parameterNode.getStringAttribute("typeHandler");        Integer numericScale = parameterNode.getIntAttribute("numericScale");        ParameterMode modeEnum = resolveParameterMode(mode);        Class<?> javaTypeClass = resolveClass(javaType);        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);        @SuppressWarnings("unchecked")        Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);        ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);        parameterMappings.add(parameterMapping);      }      builderAssistant.addParameterMap(id, parameterClass, parameterMappings);    }  }

7、parameterMapping——>configuration

生成了parameterMapping以后,放到了哪里呢,放到了configuration中
  public ParameterMap addParameterMap(String id, Class<?> parameterClass, List<ParameterMapping> parameterMappings) {    id = applyCurrentNamespace(id, false);    ParameterMap.Builder parameterMapBuilder = new ParameterMap.Builder(configuration, id, parameterClass, parameterMappings);    ParameterMap parameterMap = parameterMapBuilder.build();    configuration.addParameterMap(parameterMap);    return parameterMap;  }

8、总结

解析mapper,主要干了什么事呢?其实也没什么,就是把配置文件解析后,封装成了MappedStatement、ParameterMap、ResultMap3个东西放到了Configuration中。




0 0