在上一篇文章中我們已經知道了 SqlSessionFactory 對象的過程,但是沒有具體對 XMLConfigBuilder 類的 parse() 方法進行講解,那麼此次就通過了解 mapper 的解析過程來順便把 parse() 方法的流程給講嘍!
核心代碼就是通過 parseConfiguration() 方法解析 xml 配置文件下 <configuration/> 元素下的子元素。
敲黑板了,解析 <mappers /> 元素的方法是 mapperElement() 方法,我們現在來仔細看看 mybatis 在解析 <mappers /> 元素時到底做了什麼。
可以看到,核心代碼有四處,分別對應不同的配置形式,如下:
<!-- 註冊包下所有的 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 解析的時序圖和流程圖分別如下所示: