首先我们来看看mapper.xml文件的加载方式
第一种:基于mapper接口类具体路径配置
这种情况下,如果是非注解模式的话xml配置文件必须和这个类在同一级目录,且与Mapper类同名。项目结构类似
第二种:基于mapper类所在包的package路径配置
其实这算是第一种的一个变种。一个配置mapper类,每次新加删除比较麻烦,直接扫描mapper类所在包。项目结构类似
第三种:把 Mapper 的xml配置文件单独放置到 resources 中
和Mapper 类分开了,mapper接口位置还保持不动
但是mapper.xml被移动到了resources目录
第四种:使用spring配置加载mapper配置
可以看到,是往sqlsessionfactory中注入一系列的属性值。
(以上文章摘抄于https://blog.csdn.net/tanga842428/article/details/79285957,感谢博主的分享)
通过上面的四种配置方式,我们可以看到,mybatis有个默认潜规则:mapper接口类的名字和mapper.xml的名字是一致的。
基于上面的四种配置,可以引申出很多其他形式的配置,比如使用注解,基于实现原理再实现一遍,你就可开源xxx-mybatis-boot-start.jar类。小编们就是基于第四种实现原理,实现类一套,源码如下:
其中res1 和res2使用一个即可。由于项目架构的性质,我们使用了两个(当然还可以更多,因为mapperlocation是数组)。
铺垫做完了,我们开始今天的主题《mybatis源码分析之mapper.xml文件加载原理解析》
分析源码,关键是找对入口。首先我们来看下setMapperLocations方法的作用(resource对象传入的已经是根据正则表达式匹配到的各个xml文件了)
“Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory}configuration at runtime.”。大概意思是讲:在系统启动时,这些mapper配置文件会被合并初始化到SqlSessionFactory。接下来我们来看下SqlSessionFactory初始化做了些什么。
本文代码的切入点是bean.getObject()。一路跟踪(bean.getObject()->afterPropertiesSet()->buildSqlSessionFactory()),并会发现,真正做事的是buildSqlSessionFactory(),其作用就是创建一个SqlSessionFactory实例并返回。
该方法源码比较长,不再贴源码,只解释下里面关键名词
configuration 、configLocation: mybatis配置文件,两者只会二选一加载,例如上面示例中的mybatis-config.xml。
objectFactory mybatis对象工厂(没用过)
objectWrapperFactory mybatis对象包装类工厂(没用过)
vfs mybatis虚拟文件系统,在应用服务内,提供一个非常简单的方式访问资源(没用过)
typeAliases 类型别名
typeHandlers 类型处理器
typeAliasesPackage 类型别名包路径
plugins 插件类(后续会讲)
typeHandlers 类型处理器
typeHandlersPackage 类型处理器包路径
databaseIdProvider (官方描述MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃)
mapperLocations mapper文件地址
解析一个个mapper.xml文件,放入config,然后返回。下面看看XMLMapperBuilder是怎么解析的
第一步:new XMLMapperBuilder构造类,把xml文件解析成document。xml解析方式
第二步:xmlMapperBuilder.parse()。分析xml中的每个node,转化为对象。下面着重介绍这步
configurationElement方法负责解析mapper节点下的各个子节点,并放入configuration中。回想一下mapper.xml的结构。
我们重点介绍下buildStatementFromContext(context.evalNodes("select|insert|update|delete"))方法,该方法是解析增删改查语句生成MappedStatement,并放入configuration的mappedStatements中,解析失败的放入incompleteStatements失败列表。 注意mappedStatements是一个map,value为MappedStatement,key为什么形式呢。举个例子:如果你的接口类(之前的namespace)是com.org.userMapper,节点id为selectOne的话,该map的id为com.org.userMapper.selectOne。这样保证了不同接口类,可以使用相同名字
至此,mapper.xml的解析完成,我们在看看收尾工作是怎么做的
解析失败的记录会死循环处理,那么总的有地方触发处理这处理失败的语句。触发方法如下
该方法注释写到,当所有的mapper.xml文件加载完成,推荐只调用一次该方法,处理这些之前失败的记录。
于是查看该方法的调用栈得到
系统启动完成后,会通过event机制调用一次
作者也玩公众号,欢迎关注《JAVA之庖丁解牛》
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1xmq4bipwe3yf