番外篇-2:xml和接口相互转换逻辑

有时候我们明明只注册了UserMapper.xml资源,但是并没有注册UserMapper.java接口,但是却可以进行使用,原因是因为mybatis底层帮我们自动进行转换了,反过来也是一样的,以下就把两种情况都看下

xml自动注册接口

xml解析入口为:org.apache.ibatis.builder.xml.XMLMapperBuilder#parse

  public void parse() {
    //如果没解析过才会进行解析,其实就是用set来进行判断
    if (!configuration.isResourceLoaded(resource)) {
      //解析mapper标记,这也是最核心的一个位置
      configurationElement(parser.evalNode("/mapper"));
      //放到mapper里面去
      configuration.addLoadedResource(resource);
      //绑定到接口上,如果namespace配置的是接口的话,这也是一个规范
      bindMapperForNamespace();
    }

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


在解析完以后mybatis帮我们注册对应的接口了, bindMapperForNamespace



  private void bindMapperForNamespace() {
    //获取xml中配置的接口全类名
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class boundType = null;
      try {
        //Class.forName获取Class对象
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        //反射成功并且配置中还没有这个接口对应的信息,那么就进行注册
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          configuration.addMapper(boundType);
        }
      }
    }
  }




可见,这个过程还是比较简单的,我们只需要把xml文件中的namespace配置为接口的全类名即可

接口自动注册xml

接口自己注册位置:org.apache.ibatis.binding.MapperRegistry#addMapper


    //只有接口才会进来,因为要生成代理对象
    if (type.isInterface()) {
      //已经有了就直接抛异常
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        //生成代理对象
        knownMappers.put(type, new MapperProxyFactory(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        //解析,这里面又会把接口相关的再解析一遍,根据接口全类名去找关联的xml文件,
        //格式为 String xmlResource = type.getName().replace('.', '/') + ".xml";
        //所以要遵循它的规定
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }



等接口注册完以后它又会去注册对应的xml文件   parse方法中的


org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#loadXmlResource这个位置进行加载




  private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    //判断是不是加载过了就不重复加载
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      //拼接路径获取资源文件
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      InputStream inputStream = null;
      try {
        /**
         * xmlResource 格式为   com/zxc/study/test/mapper/UserMapper.xml     它会把接口替换为具体的xml位置
         * 所以你的xml文件就必须遵循对应的规范了,否则mybatis就无法帮你自动解析xml
         * 
         * 当然了,你也可以自己在这里定义mapper文件的地址去加载,但是原生的mybatis这里不提扩展,要改只能改源码,,一般也是不建议的
         * mybatis-spring和mybatis-plus可能覆盖了这里的方法进行可以指定吧
         */
        //或者对应的资源文件
        inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
      } catch (Exception e) {
        // ignore, resource is not required
      }
      if (inputStream != null) {
        //不为空时又调用原来的解析
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        xmlParser.parse();
      }
    }
  }




其实原理也不难,只不过这里稍微要遵循下规范,比xml自动注册接口麻烦了一点点

以上便是本篇的内容,其实就是要遵循一些规范,代码单独就这个逻辑的话也并不是很复杂

你可能感兴趣的:(mybatis源码相关,xml,mybatis,java)