MyBatis源码解析【7】接口式编程
前言
这个分类比较连续,如果这里看不懂,或者第一次看,请回顾之前的博客
http://www.cnblogs.com/linkstar/category/1027239.html
修改例子
在我们实际中我们常见的一种模式就是只是书写mybatis的接口,而并不做mybatis的实现,从而减少了代码量和一些没有必要的错误。
下面我们继续修改之前的例子。
只需要修改我们的主要测试类就可以了
}
}
从例子中我们可以看到,在mybatis中,一个没有实现类的接口可以正常的被调用,并且直接执行到相对应的sql语句。
那么mybatis是如何实现的呢?
这个问题就是我们今天需要学习的重点了。
大致运行步骤
我们依旧先从大的方向看
DemoMapper demoMapper = session.getMapper(DemoMapper.class);
首先是从我们的产品sqlsession中调用了一个getMapper传入了一个需要被调用接口的类
这个类的传入类型这边会产生疑问,为什么会传入这样一个class类型呢?
调用完成之后直接返回了一个接口,又会有疑问,难道这个接口在这个方法里面被new出来了吗?
接下来就是接口直接调用方法了。
Demo demo = demoMapper.selectDemo();
调用的方法似乎看起来和原来我们写的普通的接口没有什么不同
但是这个接口并没有实现类!!!
那么执行为什么会返回结果呢?
这个结果又是如何封装的呢?
带着这些问题我们在仔细往里面看看。
源码解析
首先进入了DefaultSqlSession

进去

然后一直往里走,

这里的代码就很神奇了
一般人到这里就看不懂了(如果你的装备还不够好的话)
首先mapperProxyFactory是什么?
从名字上面我们把它叫做映射代理工厂。
knownMappers这个呢是一个map
这个map是以class作为键,代理工厂作为值的一个map
然后第一步就是从这个map中取到我们的代理工厂
------------------------------------------------------
对于map哪里来的,map里面的值是从哪里来的?是在mybatis读取xml配置文件的时候加载的,具体我不仔细一步步看这些加载的代码因为对于我们当前的最终目的关系不大,有兴趣的可以根据下面的思路去,从
new SqlSessionFactoryBuilder().build(inputStream);开始看看配置文件加载的过程。
{
1、build中的parser.parse()
2、parseConfiguration
3、mapperElement(root.evalNode("mappers"));
4、String mapperClass = child.getStringAttribute("class");
5、else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
6、addMapper一直进去就有咯
}
------------------------------------------------------
我们先看看这个代理工厂有什么用?
return mapperProxyFactory.newInstance(sqlSession);
这个newInstance的名称是不是熟悉?没错就是反射里面的那个。我们进去看看

final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
我们先看MapperProxy这个构造方法,传入了三个参数,sqlSession,接口和一个map
然后构造方法内部成员变量进行赋值就构造完成了我们这个代理类。
然后就是重点了。
(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
看到这个是不是又有熟悉的感觉了?没错这个是动态代理的知识。
第一个参数是:类加载器
第二个参数是:接口数组
第三个参数是:代理实例的调用处理程序
由动态代理的知识我们可以知道,传入的代理类代理了这个接口。
也就是说,当这个接口的方法被调用的时候,都会先调用代理类中的invoke方法。
如果不明白为什么,证明你还需要好好学习一下动态代理的知识哦。
然后我们进入MapperProxy类(代理类)

很显然这个类实现了InvocationHandler接口
重写了invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
第一个参数:代理实例(不需要管,没用到)
第二个参数:当我们调用接口方法时,因为有代理类所以会调用invoke方法同时将调用的是什么方法传入进来。
第三个参数:是方法调用时的参数
首先我们要注意Object.class.equals(method.getDeclaringClass())
通过method.getDeclaringClass()获取的class就是我们传入的class,肯定不是object所以不可能去执行if内部的逻辑,也就是说,不会也不能调用原来方法。因为没有实现类。
原本的动态代理,在代理实例中的invoke最后我们经常见到的method.invoke();我们可以在这个方法执行的前后加入自己的逻辑,而这次mybatis之所以不能调用这个方法,是因为这个接口没有实现类所以不需要调用,直接走自己的逻辑,而自己的逻辑就是下面两行代码。
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
首先是cachedMapperMethod这个方法是返回了一个MapperMethod

我们来看看这个类是如何构造的
首先传入了一个接口mapperInterface
一个方法method
配置文件sqlSession.getConfiguration

然后根据这三个参数又创建了一个SqlCommand的类
this.command = new SqlCommand(config, mapperInterface, method);
这个类的构造方法就比较重要了,仔细看


首先通过接口的名称+方法名就构成了statementName
然后从配置文件中去寻找这个statementName
而这个东西我们写xml就应该意识到就是xml的id
那么我们就可以知道,这里所做的事情就是将我们的接口与我们的xml进行匹配绑定
也就是为什么我们执行一个对应的接口方法的时候就可以找到对应xml里面执行sql的原因了。
而且这里最后从配置文件中拿到了MappedStatement之后就从这个类中可以拿到
name = ms.getId();
type = ms.getSqlCommandType();
也就是运行的名称也就是ID和sql的类型,增删改查

然后之前除了SqlCommand还有一句
this.method = new MethodSignature(config, mapperInterface, method);
这里是在干什么呢?
进去之后就会发现一大堆的赋值,但是有一点很明显
this.returnType = method.getReturnType();
也就是说,这里构造的所谓的MethodSignature叫方法签名,就是通过传入的接口调用的方法获取返回值类型。这里要记住哦,下面还有用的。
然后我们再来看看前面两句中的后面一句,执行

return mapperMethod.execute(sqlSession, args);
execute中首先就是根据刚才获取的类型,看看是哪一种的类型的语句

我们这里是查询
这里就用到为了我们刚才所初始化的方法签名method,通过这里对返回值类型的判断,从而就能执行对应的方法了。
如果你的返回值是list那么就会调用selectList
如果你的返回值是一个那么就会调用selectOne等等
总之,返回值的不同而导致了最后调用的不同,也就最终执行了我们之前所讲过的sql语句了。
以上就是为什么我们通过getMapper方法获取接口之后,直接调用接口就可以获取到返回数据的原因了。
总结
1、明确了MyBatis面向接口式编程的全部原理。
2、整个过程有点长,如果对于代理模式和反射掌握的不熟悉的话推荐直接debug模式跟踪断点。
3、之后会尝试写一个例子来模拟整个过程让整个过程更加清晰一些。
MyBatis源码解析【7】接口式编程的更多相关文章
- Mybatis源码解析5—— 接口代理
本篇文章,可乐将为大家介绍通过接口代理的方式去执行SQL操作.话不多说,直接上图: 其实无论哪种方式,我们最终是需要找到对应的 SQL 语句,接口代理的方式就是通过 [包名.方法名] 的方式,去找到 ...
- Mybatis源码解析(一) —— mybatis与Spring是如何整合的?
Mybatis源码解析(一) -- mybatis与Spring是如何整合的? 从大学开始接触mybatis到现在差不多快3年了吧,最近寻思着使用3年了,我却还不清楚其内部实现细节,比如: 它是如 ...
- 【MyBatis源码解析】MyBatis一二级缓存
MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...
- Mybatis源码解析-DynamicSqlSource和RawSqlSource的区别
XMLLanguageDriver是ibatis的默认解析sql节点帮助类,其中的方法其会调用生成DynamicSqlSource和RawSqlSource这两个帮助类,本文将对此作下简单的简析 应用 ...
- mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)
目录 1. 简介 2. 解析 3 StrictMap 3.1 区别HashMap:键必须为String 3.2 区别HashMap:多了成员变量 name 3.3 区别HashMap:key 的处理多 ...
- mybatis源码-解析配置文件(三)之配置文件Configuration解析
目录 1. 简介 1.1 系列内容 1.2 适合对象 1.3 本文内容 2. 配置文件 2.1 mysql.properties 2.2 mybatis-config.xml 3. Configura ...
- Mybatis源码解析优秀博文
最近阅读了许久的mybatis源码,小有所悟.同时也发现网上有许多优秀的mybatis源码讲解博文.本人打算把自己阅读过的.觉得不错的一些博文列出来.以此进一步加深对mybatis框架的理解.其实还有 ...
- Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码
在文章:Mybatis源码解析,一步一步从浅入深(一):创建准备工程,中我们为了解析mybatis源码创建了一个mybatis的简单工程(源码已上传github,链接在文章末尾),并实现了一个查询功能 ...
- Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析
在上一篇文章Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例中我们谈到了properties,settings,envir ...
- Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取
在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao ...
随机推荐
- Fiddler 手机抓包 手机联网异常解决方案
Fiddler在电脑里已经闲置很久了,之前看是不是服务器返回问题都是连上AS看输出的log,但是终归不如直接抓包来的方便 昨天搞了一下午,手机跟电脑都是连的公司的wifi,手机设置电脑ip和端口的网络 ...
- win10下使用wget
一.下载 官网:http://gnuwin32.sourceforge.net/packages/wget.htm 下载地址:http://downloads.sourceforge.net/gnuw ...
- Python3实战系列之六(获取印度售后数据项目)
问题:续接上一篇.说干咱就干呀,勤勤恳恳写程序呀! 目标:此篇我们试着把python程序打包成.exe程序.这样就可以在服务器上运行了.实现首篇计划列表功能模块的第三步: 3..exe文件能在服务器上 ...
- Html5学习笔记:图片上传
图片上传是业务需求中常见的功能,最基础的是单图片的上传.比较复杂的多图片上传,都是基于单图片上传. Form表单上传 h5的原生表单上传图片,代码如下: <!DOCTYPE html> & ...
- django学习install apps注册错了的影响
今天在学习例子的时候 不注意吧settings.py里面的INSTALL APPS 的APP应用名称写错了 应该是blog 写成了myblog 结果导致python manage.py makemi ...
- 手动上传图片到nginx下可访问,程序上传后访问图片报403
1. 首先查看文件权限 2. 初步确定是服务器权限问题 2.1 解决方案一:更改文件权限 2.2 解决方案二:修改nginx运行用户 1. 首先查看文件权限 #指令如下 ls -l 2. 初步确定是服 ...
- suse下修改主机名
export HOSTNAME=主机名 echo $HOSTNAME>/etc/HOSTNAME /etc/rc.d/boot.localnet stop /etc/rc.d/boot.loca ...
- Android studio提速配置
1. C:\Program Files\Android\Android Studio\bin studio64.exe.vmoptions 2.创建 gradle.properties 配置文件
- shell中与C语言中的区别
shell中为啥与C语言有区别呢?弄成一样的不是很好么,其实不然,shell提供很多操作,这些操作不单单是执行程序或者命令,在很多时候是执行脚本的,简单的shell就是脚本编程,它的主要目的是处理文件 ...
- pgsqls修改表字段长度
alter table T_RPACT_PROTO_EDIT_RECORD alter column remark type VARCHAR(1024); 需要注意type关键字