前言

这个分类比较连续,如果这里看不懂,或者第一次看,请回顾之前的博客

http://www.cnblogs.com/linkstar/category/1027239.html

修改例子

在我们实际中我们常见的一种模式就是只是书写mybatis的接口,而并不做mybatis的实现,从而减少了代码量和一些没有必要的错误。

下面我们继续修改之前的例子。

只需要修改我们的主要测试类就可以了

public class MainTest { public static void main(String[] args) throws Exception { SqlSessionFactory sqlSessionFactory = SqlSessionFactoryBuilderTest.getSqlSessionFactory(); SqlSession session = sqlSessionFactory.openSession(); DemoMapper demoMapper = session.getMapper(DemoMapper.class); Demo demo = demoMapper.selectDemo(); System.out.println(demo.getValue()); /*try { Demo demo = (Demo) session.selectOne("com.xex.dao.mapper.DemoMapper.selectDemo"); System.out.println(demo.getValue()); } finally { session.close(); }*/

    }
}

从例子中我们可以看到,在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】接口式编程的更多相关文章

  1. Mybatis源码解析5—— 接口代理

    本篇文章,可乐将为大家介绍通过接口代理的方式去执行SQL操作.话不多说,直接上图: 其实无论哪种方式,我们最终是需要找到对应的 SQL 语句,接口代理的方式就是通过 [包名.方法名] 的方式,去找到 ...

  2. Mybatis源码解析(一) —— mybatis与Spring是如何整合的?

    Mybatis源码解析(一) -- mybatis与Spring是如何整合的?   从大学开始接触mybatis到现在差不多快3年了吧,最近寻思着使用3年了,我却还不清楚其内部实现细节,比如: 它是如 ...

  3. 【MyBatis源码解析】MyBatis一二级缓存

    MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...

  4. Mybatis源码解析-DynamicSqlSource和RawSqlSource的区别

    XMLLanguageDriver是ibatis的默认解析sql节点帮助类,其中的方法其会调用生成DynamicSqlSource和RawSqlSource这两个帮助类,本文将对此作下简单的简析 应用 ...

  5. mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)

    目录 1. 简介 2. 解析 3 StrictMap 3.1 区别HashMap:键必须为String 3.2 区别HashMap:多了成员变量 name 3.3 区别HashMap:key 的处理多 ...

  6. mybatis源码-解析配置文件(三)之配置文件Configuration解析

    目录 1. 简介 1.1 系列内容 1.2 适合对象 1.3 本文内容 2. 配置文件 2.1 mysql.properties 2.2 mybatis-config.xml 3. Configura ...

  7. Mybatis源码解析优秀博文

    最近阅读了许久的mybatis源码,小有所悟.同时也发现网上有许多优秀的mybatis源码讲解博文.本人打算把自己阅读过的.觉得不错的一些博文列出来.以此进一步加深对mybatis框架的理解.其实还有 ...

  8. Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码

    在文章:Mybatis源码解析,一步一步从浅入深(一):创建准备工程,中我们为了解析mybatis源码创建了一个mybatis的简单工程(源码已上传github,链接在文章末尾),并实现了一个查询功能 ...

  9. Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析

    在上一篇文章Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例中我们谈到了properties,settings,envir ...

  10. Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取

    在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao ...

随机推荐

  1. CSS学习总结4:派生选择器学习总结

    派生选择器:通过依据元素在其位置的上下文关系来定义样式,你可以使标记更加简洁.派生选择器中一共分为三种:后代选择器.子元素选择器.相邻兄弟选择器. 1.初识派生选择器 实例:你希望列表中的 stron ...

  2. pandas 导入导出

    pandas可以读取与存取的资料格式有很多种,像csv.excel.json.html与pickle等… 1.读取csv import pandas as pd #加载模块 #读取csv data = ...

  3. AUTEL MaxiSys MS908S Pro MS908SP Diagnostic Platform

    AUTEL MaxiSys MS908S Pro Description : One of the MaxiSys series devices, the MS908S Pro Diagnostic ...

  4. android 平台 java和javascript 通信问题 A WebView method was called on thread 'JavaBridge'.

      java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'JavaBridge ...

  5. mysql error#1251客户端版本过低

    mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER; Query OK, 0 ...

  6. solr7.7.0搜索引擎使用(二)(添加搜索)

    一.安装完毕之后,需要为solr添加core,每一个搜索server就是一个core,solr可以有很多core,我们需要创建一个core用于我们的搜索 添加core的方式有两种: 第一种进入solr ...

  7. HTML语言

    复习: 1.Web项目的部署结构  静态Web技术(客户端技术):提供的内容任何人在任何时间访问都是一样的 HTML/CSS/JS/Flash.... 动态Web技术(服务器端技术):提供的内容不同人 ...

  8. UML类图中箭头和线条的含义和用法

    UML类图中箭头和线条的含义和用法 在学习UML过程中,你经常会遇到UML类图关系,这里就向大家介绍一下UML箭头.线条代表的意义,相信通过本文的介绍你对UML中箭头.线条的意义有更明确的认识. AD ...

  9. Unittest中TestCase类中定义的几个特殊方法

    1.setUp():每个测试方法运行前运行,测试前的初始化工作: 2.tearDown():每个测试方法运行结束后运行,测试后的清理工作: 3.setUpClass():所有的测试方法运行前运行,单元 ...

  10. 走进JDK(十)------HashMap

    有人说HashMap是jdk中最难的类,重要性不用多说了,敲过代码的应该都懂,那么一起啃下这个硬骨头吧!一.哈希表在了解HashMap之前,先看看啥是哈希表,首先回顾下数组以及链表数组:采用一段连续的 ...