天天看点

Mybatis 源码分析:Mapper 解析过程

在上一篇文章中我们已经知道了 SqlSessionFactory 对象的过程,但是没有具体对 XMLConfigBuilder 类的 parse() 方法进行讲解,那么此次就通过了解 mapper 的解析过程来顺便把 parse() 方法的流程给讲喽!

Mybatis 源码分析:Mapper 解析过程

核心代码就是通过 parseConfiguration() 方法解析 xml 配置文件下 <configuration/> 元素下的子元素。

Mybatis 源码分析:Mapper 解析过程

敲黑板了,解析 <mappers /> 元素的方法是 mapperElement() 方法,我们现在来仔细看看 mybatis 在解析 <mappers /> 元素时到底做了什么。

Mybatis 源码分析:Mapper 解析过程

可以看到,核心代码有四处,分别对应不同的配置形式,如下:

<!-- 注册包下所有的 mapper -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>
           
<!-- Using classpath relative resources -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
           
<!-- Using url fully qualified paths -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
           
<!-- Using mapper interface classes -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
           

这里只说明两种配置形式的解析,一种是 package;一种是 resource。因为 url 配置形式跟 resource 形式差不多,class 配置形式较为简单,可以自行了解下。

  • package 配置形式

    首先它回调用 Configuration 对象的 addMappers() 方法,然后此方法会委托给 MapperRegistry 对象的 addMappers() 方法。

    public void addMappers(String packageName) {
    	addMappers(packageName, Object.class);
    }
               
    可以看到之后又会调用它另外一个 addMappers 方法,真是踏破铁鞋才能找到真正执行逻辑的地方啊!
    public void addMappers(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
        // 查找此包下所有的类
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
        for (Class<?> mapperClass : mapperSet) {
          // 注册 mapper 类
          addMapper(mapperClass);
        }
    }
               
    mybatis 会对此包下所有的 java 文件注册到 MapperRegistry 中去。
    public <T> void addMapper(Class<T> type) {
        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<T>(type));
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
          } finally {
            if (!loadCompleted) {
              knownMappers.remove(type);
            }
          }
        }
    }
               
    其中需要注意的代码是 knownMappers.put(type, new MapperProxyFactory(type)),knowMappers 的类型为 HashMap,key 为 mapper 接口的全限定名,value 为 生成 mapper 接口代理实现类的工厂类。它的类图为:
    Mybatis 源码分析:Mapper 解析过程
  • resource 配置形式
    Mybatis 源码分析:Mapper 解析过程
    这里需要注意的是,parser.evalNode("/mapper") 是对 mapper 接口对应的 xml 文件中的 <mapper /> 元素进行构建,然后 configurationElement() 方法是设置 namespace 和对整个文件进行解析。
    Mybatis 源码分析:Mapper 解析过程
    之后得到 namespace 对应的 Class 类型,然后跟上述过程中的最后一步就是一样了,注册到 MapperRegistry。

整个 mapper 解析的时序图和流程图分别如下所示:

Mybatis 源码分析:Mapper 解析过程
Mybatis 源码分析:Mapper 解析过程

继续阅读