1.通过调试,session调用的getMapper是其实现类DefaultSQLSession中的

        //1.读取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建 SqlSessionFactory 的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = builder.build(in);
//4.使用 SqlSessionFactory 生产 SqlSession 对象
SqlSession session = factory.openSession();
//5.使用 SqlSession 创建 dao 接口的代理对象
IBookDao bookDao = session.getMapper(IBookDao.class);
//6.使用代理对象执行查询所有方法
List<Book> books = bookDao.selectAll();
for (Book book : books) {
System.out.println(book);
}
//7.释放资源
session.close();
in.close();

2.在DefaultSQLSession类中getMapper方法如下,里面调用Configuration类的泛型方法<T> T getMapper(Class<T> type)

@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}

3.在Configuration类中查看getMapper(type, sqlSession)方法,里面调用的是MapperRegistry类里面的getMapper(type, sqlSession)方法。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}

4.进入MapperRegistry类里面查看getMapper(type, sqlSession)方法。里面通过属性knownMappers里面的方法get(Object key)获取MapperProxyFactory<T>泛型类,使用这个类里面的newInstance(SqlSession sqlSession)方法。

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}

5.进入MapperProxyFactory<T>泛型类,查看newInstance(SqlSession sqlSession)方法如下;这里面生成一个MapperProxy<T>对象mapperProxy,然后使用T newInstance(MapperProxy<T> mapperProxy)实现动态代理。其中MapperProxy<T>类实现了接口InvocationHandler。重写了Object invoke(Object proxy, Method method, Object[] args)方法

 protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
} public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}

6.查看MapperProxy<T>类里的invoke方法.

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try { if (Object.class.equals(method.getDeclaringClass())) {//并不是每个方法都需要调用代理对象进行执行,如果这个方法是Object中通用的方法,则无需执行
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {//判断是不是接口默认方法,1.8之后接口就有默认方法了
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//从缓存中获取MapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行方法对应的 SQL 语句
return mapperMethod.execute(sqlSession, args);
}

7.查看MapperMethod 类的execute方法,这个方法是执行sql的主要方法,根据我写的程序这里是调用executeForMany方法,实际上这个方法DefaultSqlSession类里面的selectList方法。

 public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
//insert语句的处理逻辑
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
//update语句的处理逻辑
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
//delete语句的处理逻辑
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
//select语句的处理逻辑
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}

在这里,动态代理过程就完成了。大致执行过程是  sqlSession.getMapper()【这个sqlSession实际上是 DefaultSqlSession】-----调用---->Configuration.getMapper()-----调用--->MapperRegistry.getMapper() ------调用-->MapperProxyFactory.newInstance() -------调用----> Proxy.newProxyInstance()【其中的InvocationHandler接口由实现类MapperProxy代替】------invoke----->MapperMethod.execute()-----调用----->DefaultSqlSession.相应方法

Mybatis---01Mybatis动态代理过程分析的更多相关文章

  1. Mybatis mapper动态代理的原理详解

    在开始动态代理的原理讲解以前,我们先看一下集成mybatis以后dao层不使用动态代理以及使用动态代理的两种实现方式,通过对比我们自己实现dao层接口以及mybatis动态代理可以更加直观的展现出my ...

  2. (十二)mybatis之动态代理

    mybatis之动态代理的应用 在前文(https://www.cnblogs.com/NYfor2018/p/9093472.html)我们知道了,Mybatis的使用需要用到Mapper映射文件, ...

  3. MyBatis学习(三)MyBatis基于动态代理方式的增删改查

    1.前言 上一期讲到MyBatis-Statement版本的增删改查.可以发现.这种代码写下来冗余的地方特别多.写一套没啥.如果涉及到多表多查询的时候就容易出现问题.故.官方推荐了一种方法.即MyBa ...

  4. 【转载】由浅入深分析mybatis通过动态代理实现拦截器(插件)的原理

    转自:http://zhangbo-peipei-163-com.iteye.com/blog/2033832?utm_source=tuicool&utm_medium=referral 我 ...

  5. Mybatis使用动态代理实现拦截器功能

    1.背景介绍 拦截器顾名思义为拦截某个功能的一个武器,在众多框架中均有“拦截器”.这个Plugin有什么用呢?或者说拦截器有什么用呢?可以想想拦截器是怎么实现的.Plugin用到了Java中很重要的一 ...

  6. JAVA框架 Spring 和Mybatis整合(动态代理)

    一.使用传统方式的dao的书写方式,不建议.目前采用的是动态代理的方式交给mybatis进行处理. 首先回顾下动态代理要求: 1)子配置文件的中,namespace需要是接口的全路径,id是接口的方法 ...

  7. spring如何管理mybatis(一) ----- 动态代理接口

    问题来源 最近在集成spring和mybatis时遇到了很多问题,从网上查了也解决了,但是就是心里有点别扭,想看看到底怎么回事,所以跟了下源码,终于发现了其中的奥妙. 问题分析 首先我们来看看基本的配 ...

  8. Mybatis 之动态代理

    使用Mybatis 开发Web 工程时,通过Mapper 动态代理机制,可以只编写接口以及方法的定义. 如下: 定义db.properties driver=oracle.jdbc.OracleDri ...

  9. Spring 整合Mybatis Mapper动态代理方法

    先看项目目录结构 很清爽了 最重要的Spring的核心配置文件,看一下 <?xml version="1.0" encoding="UTF-8"?> ...

  10. Mybatis Mapper动态代理方式 typeAliases 别名的使用

    目录结构及配置文件与原始dao方法相比更简便 只需一个UserMapper的接口,放在一起的配置文件,配置文件中namespace的地址确定jdk动态代理的对象 <?xml version=&q ...

随机推荐

  1. 永远不要在代码中使用“User”这个单词

    ​ 当你意识到你在项目开始时做的轻量.简单的设想竟然完全错了时,你已经用了六个月的时间投入到这个项目上.现在你需要解决这些问题,才能让这个系统继续运行下去,你发现你用在这个项目上的精力远远超出了你的预 ...

  2. Python3基础数据类型(数字、字符串、元组、列表、集合、字典)

    笔记参考了菜鸟教程 Python 中的变量不需要声明,赋值才创建.赋值给变量的是什么类型变量就是什么类型 多个变量赋值 1 a, b, c = 1, 2, "runoob" 标准数 ...

  3. Unity Shader学习笔记-1

    本篇文章是对Unity Shader入门精要的学习笔记,插图大部分来自冯乐乐女神的github 如果有什么说的不正确的请批评指正 目录 渲染流水线 流程图 Shader作用 屏幕映射 三角形遍历 两大 ...

  4. HTML+CSS系列:登录界面实现

    一.效果 二.具体实现 1.index.html <!DOCTYPE html> <html> <head> <meta charset="utf- ...

  5. 《RESTful Web APIs》书中有一段POST API示例,现实中我们如何测试这个示例?书中没有说,Let's try it!

    <RESTful Web APIs>书中有一段POST API示例: I then send the filled-out template as part of an HTTP POST ...

  6. Matlab绘制子图subplot使用攻略

    参考:https://jingyan.baidu.com/article/915fc414ad794b51394b20e1.html Matlab绘制子图subplot使用攻略 听语音 原创 | 浏览 ...

  7. arduino 动态内存不足问题

    亲测有用, 参考:https://blog.csdn.net/weixin_33915554/article/details/86975847 如果在代码中使用到一个数组,但是数组容量超过20000个 ...

  8. 《C++ primer plus》第3章练习题

    注:有的题设条件自己改动了一下,比如英寸英尺改成米和厘米,部分题目自己加了点额外要求. 1.要求用户输入身高(m),用下划线提示用户输入,将身高转化成"米"加"厘米&qu ...

  9. JavaScript打印给定区间年份的闰年

    要求: 用户输入需要判断的年份区间,开始年份和结束年份,输出该区间内所有的闰年. 代码实现: function isRunYear(year) { // 是闰年返回true,否则返回false var ...

  10. 0xctf[No parameters readfile](魔改版[GXYCTF2019]禁止套娃)

    阅读本文前建议先阅读本站中的另一篇文章:[GXYCTF2019]禁止套娃 重要参考链接:http://www.heetian.com/info/827 Leon师傅魔改了[GXYCTF2019]禁止套 ...