博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis 源码学习(一) Mapper 注册
阅读量:4217 次
发布时间:2019-05-26

本文共 3598 字,大约阅读时间需要 11 分钟。

问题:mybatis中mapper接口和xml文件是如何对应起来的。

在mybatis-config.xml中配置一个mapper

是最简单最直接的方式了。这样我们就可以通过SqlSession来获取这个mapper了。

表明上我们获取mapper是通过sqlSession的getMapper函数,稍微看一下源码,发现实际sqlSession调用的Configuration的getMapper函数。继续跟下去,Configuration又调用MapperRegistry类的getMapper函数。

看来MapperRegistry才是最终管理mapper注册和获取的类。

那么mybatis是怎么通过一个xml文件,来注册一个对应的mapper的呢。

研究最简单的加载方式:

public class App {    public static void main( String[] args )    {        String resource = "mybatis-config.xml";        try {            InputStream inputStream = Resources.getResourceAsStream(resource);            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);            SqlSession sqlSession = sqlSessionFactory.openSession();            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        } catch (IOException e) {            e.printStackTrace();        }    }}

来看一下调用栈:

当我们看到 下面的这个函数实现,心里就豁然开朗了。xml的各种配置方式都在这。

private void mapperElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        if ("package".equals(child.getName())) {          String mapperPackage = child.getStringAttribute("name");          configuration.addMappers(mapperPackage);        } else {          String resource = child.getStringAttribute("resource");          String url = child.getStringAttribute("url");          String mapperClass = child.getStringAttribute("class");          if (resource != null && url == null && mapperClass == null) {            ErrorContext.instance().resource(resource);            InputStream inputStream = Resources.getResourceAsStream(resource);            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());            mapperParser.parse();          } else if (resource == null && url != null && mapperClass == null) {            ErrorContext.instance().resource(url);            InputStream inputStream = Resources.getUrlAsStream(url);            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());            mapperParser.parse();          } else if (resource == null && url == null && mapperClass != null) {            Class
mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }   
 

再往下看 当resource不为空的时候,最终会去执行mapperParser.parse();这个函数中关键的一个地方,bindMapperForNamespace();

private void bindMapperForNamespace() {    String namespace = builderAssistant.getCurrentNamespace();    if (namespace != null) {      Class
boundType = null; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType); } } } }

"namespace" 是关键,就是通过这个命名空间来找到对应的接口的,所以要注册一个mapper最终还有要找到这个接口。

原来怀疑是可以通过xml文件的内容直接反射来注册一个mapper的想法是错误的!

最后看一下完整的调用栈:

你可能感兴趣的文章
C++中模版的使用
查看>>
TCP链接(三次握手)和释放(四次握手)
查看>>
面试中各类二叉树题型的整理
查看>>
快速排序
查看>>
插入算法
查看>>
希尔排序
查看>>
选择排序
查看>>
归并排序
查看>>
归并排序
查看>>
排序链表
查看>>
插入排序进行链表排序
查看>>
响应时间和周转时间区别
查看>>
spring 引入外部文件 配置数据源
查看>>
c++ 经典习题源码
查看>>
Hex Editor Neo 查看各种进制数
查看>>
android 实现倒计时功能
查看>>
android Button 的绑定,监听及其背景的改变
查看>>
android 开启闪关灯手电筒的实现
查看>>
android canvas 画布
查看>>
android 实现各activity之间的跳转
查看>>