Mybatis学习(三)
typeHandlers
typeHandlers的解析入口在这里org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
跟之前的别名解析一样,也是分两种方式,一种是基于package的扫描,一种是一个一个的配置,处理diam如下:
看下最后比较关键的代码吧:
其实typeHandlers的注册就是Mybatis维护了一个javaType->jdbcType->typeHandler的映射关系,值的注意的是,这个映射关系最后并没有放到Configuration
中,而是就存在于TypeHandlerRegistry
上
mappers
mappers是xml中最后被解析的节点
mappers的处理也是分为package扫描和单条处理
其中单条处理时优先级顺序:
resource
>url
>classes
mapper处理的核心类是org.apache.ibatis.binding.MapperRegistry
,在上面configuration.addMappers时,内部调用的就是MapperRegister的方法,Configuration
中关于Mapper的方法如下:
package的扫描大家的处理方式都一样,就不说了,当获取到具体的class时,Mybatis的处理如下:
上面的代码中有两个重点
konwMappers
konwMappers
是type
与MapperProxyFactory
的映射集合
然后我们看下这个代码,在Configuration
中有个getMapper的方法,传入type和SqlSession
然后看下这个方法都被那些类调用了:
再随便挑一个看看:
getMapper这个方法是被定义在org.apache.ibatis.session.SqlSession#getMapper
上面的,所有实现了SqlSession
接口的类都会有这个方法
在一般的代码中,我们的调用可能是这样的sqlSession.getMapper(xxxMapper.class)
,而这个调用最后会调用到这里:org.apache.ibatis.binding.MapperRegistry#getMapper
其实也不复杂,就是根据传入的type到knowMappers中去找对应的factory,找不到就抛异常,找到了,就通过工厂类创建一个新的对象,因为传入的接口,所以这里默认使用的是JDK的生成代理的方式:
总结
- mybatis的代理是基于JDK的
- 代理类与代理工程被存储在
org.apache.ibatis.binding.MapperRegistry#knownMappers
中,最后Configuration
上会持有一个MapperRegistry
的引用 - 虽然
getMapper
方法是定义在SqlSession
上面的,但是其实是获取到Configuration
对象,然后通过Configuration
再获取Mapper的代理类
解析Mapper上面的注解
Mapper上注解的解析主要使用的是org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseStatement
,关键点在于所有的select|update|insert|delete最后都会被转化为MappedStetment对象,语句的解析主要参考xml的解析配置吧。
主要的思想是:将结构化的XML节点转化为树形结构
以一段xml为例:
<select id="xxx" parameterType="java.util.Map">
select id,code,name from T_XXX
<trim prefix="WHERE" prefixOverrides="AND|OR">
<if test="parentId != null">
and parentId=#{parentId}
</if>
<if test="searchKey != null">
and (code like #{searchKey} or name like #{searchKey})
</if>
</trim>
</select>
经过Mybatis的解析,会生成如下的树形对象
解析的过程如下:
-
XMLScriptBuilder
在初始化时,会调用下面的方法注册xml节点的解析器
-
解析xml节点
大体的逻辑是- 创建contents列表,contents存储当前xml节点的所有子节点的解析结果
- 循环所有的xml子节点
- 如果当前xml子节点是文本类型的节点,那么就创建一个TextSqlNode对象
- 判断这个TextSqlNode对象是否是一个动态的sql节点(其实就是判断有无占位符)
- 如果是动态节点,那就直接装入contents列表中
- 如果不是动态节点,那就创建一个StaticTextSqlNode,装入contents中
- 如果当前的xml子节点是element类型的
- 从nodeHandlerMap中查找标签的处理类
- 如果找不到处理类,则抛出异常,如果找到了,就用处理类来处理当前节点
具体的看下trim标签的处理逻辑
这样经过循环+递归,<select>
标签就会被解析成MappedStatment对象
总结
利用xml结构化的特点,可以将xml解析为树形对象再做处理。以前对于xml仅仅是通过doc4j+xpath处理node对象,从来没想过先将xml解析为树形对象再做处理,学习到了。