Mybatis 源碼分析:Mapper 解析過程

上一篇文章中我們已經知道了 SqlSessionFactory 對象的過程,但是沒有具體對 XMLConfigBuilder 類的 parse() 方法進行講解,那麼此次就通過了解 mapper 的解析過程來順便把 parse() 方法的流程給講嘍!
解析 xml
核心代碼就是通過 parseConfiguration() 方法解析 xml 配置文件下 <configuration/> 元素下的子元素。
解析
敲黑板了,解析 <mappers /> 元素的方法是 mapperElement() 方法,我們現在來仔細看看 mybatis 在解析 <mappers /> 元素時到底做了什麼。
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 接口代理實現類的工廠類。它的類圖爲:
    類圖

  • resource 配置形式
    解析
    這裏需要注意的是,parser.evalNode("/mapper") 是對 mapper 接口對應的 xml 文件中的 <mapper /> 元素進行構建,然後 configurationElement() 方法是設置 namespace 和對整個文件進行解析。
    解析
    之後得到 namespace 對應的 Class 類型,然後跟上述過程中的最後一步就是一樣了,註冊到 MapperRegistry。

整個 mapper 解析的時序圖和流程圖分別如下所示:
時序圖
流程圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章