分析源码,我们还是从编程式的 demo 入手
InputStream inputStream = Resources.getResourceAsStream(resource);
//步骤1
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//步骤2
SqlSession session = sqlSessionFactory.openSession();
//步骤3
BlogMapper mapper = session.getMapper(BlogMapper.class);
//步骤4
Blog blog = mapper.selectBlogById(1);
把文件读取成流的这一步我们就省略了。所以下面我们分成四步来分析.
1.第一步
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
我们通过建造者模式创建一个工厂类,配置文件的解析就是在这一步完成 的,包括 mybatis-config.xml 和 Mapper 适配器文件。
问题:解析的时候怎么解析的,做了什么,产生了什么对象,结果存放到了哪里。 解析的结果决定着我们后面有什么对象可以使用,和到哪里去取。
答:在这一步,我们主要完成了 config 配置文件、Mapper 文件、Mapper 接口上的注 解的解析。 我们得到了一个最重要的对象 Configuration,这里面存放了全部的配置信息,它在 属性里面还有各种各样的容器。 最后,返回了一个 DefaultSqlSessionFactory,里面持有了 Configuration 的实例。
2.第二步
SqlSession session = sqlSessionFactory.openSession();
通过 SqlSessionFactory 创建一个 SqlSession。
问题:SqlSession 是用来操作数据库的,返回了什么实现类,除了 SqlSession,还 创建了什么对象,创建了什么环境?
答:创建会话的过程,我们获得了一个 DefaultSqlSession,里面包含了一个 Executor,它是 SQL 的执行者。
3.第三步
BlogMapper mapper = session.getMapper(BlogMapper.class);
获得一个 Mapper 对象。
问题:Mapper 是一个接口,没有实现类,是不能被实例化的,那获取到的这个 Mapper 对象是什么对象?为什么要从 SqlSession 里面去获取?为什么传进去一个接 口,然后还要用接口类型来接收?
答:
现在我们已经有一个 DefaultSqlSession 了,必须找到 Mapper.xml 里面定义的 Statement ID,才能执行对应的 SQL 语句。 找到 Statement ID 有两种方式:一种是直接调用 session 的方法,在参数里面传入 Statement ID,这种方式属于硬编码,我们没办法知道有多少处调用,修改起来也很麻 烦。另一个问题是如果参数传入错误,在编译阶段也是不会报错的,不利于预先发现问 题
Blog blog = (Blog) session.selectOne("com.gudu.mapper.BlogMapper.selectBlogById ", 1);
所以在 MyBatis 后期的版本提供了第二种方式,就是定义一个接口,然后再调用 Mapper 接口的方法。 由于我们的接口名称跟 Mapper.xml 的 namespace 是对应的,接口的方法跟 statement ID 也都是对应的,所以根据方法就能找到对应的要执行的 SQL。
BlogMapper mapper = session.getMapper(BlogMapper.class);
总结:
获得 Mapper 对象的过程,实质上是获取了一个 MapperProxy 的代理对象。 MapperProxy 中有 sqlSession、mapperInterface、methodCache
第四步
Blog blog = mapper.selectBlogById(1);
调用接口方法。
问题:我们的接口没有创建实现类,为什么可以调用它的方法?那它调用的是什么 方法?它又是根据什么找到我们要执行的 SQL 的?也就是接口方法怎么和 XML 映射器 里面的 StatementID 关联起来的