上篇博文介绍了Mybatis 执行流程全貌 非常重要。本博文mybatis启动过程是如何解析配置文件的。
这还得从Spring的配置文件中SqlSessionFactoryBean 创建SqlsessionFactory说起。
该Bean实现了InitializingBean【又是一个Spring的拓展点】在Spring实例化设置和属性之后会触发afterPropertySet的调用。
每个mapper.xml文件对一个XmlMapperBuilder.parse()
如下图有一堆以Builder为后缀的类,该部分类继承关系较为复杂,在跟踪代码的时候父子类之间的方法来回调用,为了不被mybatis的套路,所以配上几张图来有个大致的理解全貌。
看下Mapper.xml的解析类XmlMapperBuilder核心解析方法:
方法见名知意:
解析 < paramenterMap>标签
解析 < resultMap> 标签
解析 < select> < insert> < update> < delete>标签
这个几个都是同一套路:以 seclect 标签解析为例:
标签 < select id=”myId” resultMap=”BaseResultMap” parameterType=”java.lang.Long”>
方法 XmlMapperStatmentBuilder.buildStatementFromContext(List< Xnode>)
套路如下:
1. 解析完标签的属性如:resultMap,id,parameterType之后
2. builderAssistant.addMappedStatement(attrVal1,attrVal2...)
3. MappedStatement.Builder statementBuilder(xxx);
4. MappedStatement statement = statementBuilder.build();
5. configuration.addMappedStatement(statement);
就是这个套路
第二步的addxxxx 方法(本例addMappedStatement)的特点是一大堆的入参属性,带着这些参数
创建一个标签对应的模型类,然后通过各自的内部类Builder创建相应的对象,在add到Configuration中
比如< select> < insert>创建MapperStatement,
维护一个内部类Builder,这个builder.build方法创建相应MapperStatement。
builderAssistant.addParameterMap(attrVal1,attrVal2...);《paramenterMap》的属性
再比如< resultMap> ResultMap,
builderAssistant.addResultMap(attrVal1,attrVal2...);//《resultMap》的属性
维护一个内部类Builder,这个builder.build方法创建相应ResultMap。
继续以< select> 为例:
上面的步骤只是将标签和属性解析完成,但是我们的业务Sql还没有解析、拼装,sql有可能还是动态的。
上面的解析过程中有一行代码:
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
看到select insert 等标签内容和子标签的解析使用XmlScriptBuilder
XmlScriptBuilder三个成员
private XNode context;//当前< select>标签
private boolean isDynamic;
private Class<?> parameterType;
成员context就是当前的select标签–sqlNode
方法返回List< SqlNode>稍后举例说明这里边装的是什么内容。
parseDynamicTags的入参node就是context 当前标签select
其实处理动态sql的代码:
NodeHandler nodeHandler = nodeHandler(nodeName)
handler.handleNode(child, contents);//方法内部是个递归的
每个handler都会处理一个动态标签,以if标签为例:
处理过程:
递归调用parseDynamicTags(当前node,targetContents)
为了更好的说明一条真实的Sql:
这里我们一条Sql为例说明返回值List< SqlNode>的内容:
<select id="batchQueryBySellerIds" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List" />
FROM
hrd_demand_matching hdm
WHERE hdm.demand_id = #{demandId}
AND hdm.seller_member_id in
<foreach collection="sellerMemberIdLs" item="sellerMemberId"
open="(" close=")" separator=",">
#{sellerMemberId}
</foreach>
</select>
我们的业务sql遇到标签就会生成一个SqlNode include除外,
将解析之后拆分【遇到标签就拆】生成SQLNode之后放入到List< SqlNode> 然后传给MixedSqlNode的构造函数,名字也好混合的SqlNode集合,会产生下图到结构:
第五个sqlNode就是递归产生的。
其实每个Sqlnode都对应一个处理器下图是对应关系:
handler.handleNode(child, contents);//方法内部是个递归的
至此SqlSource也创建出来了,只有一个动态标签创建DynamicSqlSource装有MixedSqlNode。
此时并没有sql产生,sql的产生需要参数来决定最终sql,参数决定sql的样子,这个需要在运行的时候才能获取,本博文是在启动过程中的分析。