mytatis解析xml的过程01

###描述一下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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值