2.2Mybatis——代理与SQL映射

2.2Mybatis——代理与SQL映射

  • 1.代理模式
  • 2.如何执行接口方法
    • 示例demo
  • 3.Mybatis是如何做的
    • 3.1猜想
    • 3.2源码探究
  • 梳理与总结

合集总览:Mybatis框架梳理   


“Java中非静态方法的运行需要实例对象才能运行(即对象点方法),Mybatis中的Mapper都是接口,也没有实现类,那么接口中的方法怎么就被调用执行了呢?”

这是当时用Mybatis时最困扰我的一个问题,搜的资料博客中,大多来一句“动态代理”一笔带过。留我一人风中凌乱,难道"动态代理"四个字这么形象生动、易于理解吗…
也因为这个疑问,了解了代理模式,动态代理,以及Mybatis如何使用动态代理完成mapper接口方法的调用。

1.代理模式

这里简单说一下,例子就不举了,网上都是。说一下工作中的使用场景,一般是某个类无法修改或不敢改动,那就在目标类外面包一层代理类,即不用修改目标类,又可以对目标类做功能的增强。其原理就是:通过实现和目标类相同的接口来平替目标类,然后通过构造函数获取目标类的对象引用,增强的功能由代理类完成,核心的逻辑依旧通过目标类进行调用。(我记得好像写过对应的设计模式笔记,感兴趣的可以翻一下)

2.如何执行接口方法

前面提到的代理模式和文章开头的问题有关系吗?当然有关系,如果你理解了什么是代理,文章开头的问题就可以通过代理来实现。
即使接口没有实现类,也可以通过InvocationHandler为其创建代理类,通过代理类执行接口方法。这是java反射包下的一个接口,可以为接口生成代理对象。简单代码示例:

示例demo

// 定义接口
public interface Interface01 {
    String m1();
    int m2();
}
// 定义调用处理器
public class Interface01Handler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("m1")) {
            return "hello world";
        }
        else  {
            return 1024;
        }
    }
}
// 创建代理对象测试
public static void main(String[] args) {
	// 生成代理对象
    Object proxy = Proxy.newProxyInstance(Interface01.class.getClassLoader(),
            new Class[]{Interface01.class}, new Interface01Handler());

    if(proxy instanceof Interface01) {
        // 调用接口方法
        Interface01 a = (Interface01) proxy;
        System.out.println(a.m1());
        System.out.println(a.m2());
    }
}

上述代码中,接口并没有实现类,但接口方法可以被调用。当然你也可以理解成:接口是以另一种形式被实现,接口方法以另一种形式被定义。这种在JVM运行期间动态的为接口创建代理对象的过程,我们称为JDK动态代理。

3.Mybatis是如何做的

通过反射可以为接口创建代理对象。知道了Java中的这个机制后,回到Mybatis的疑问,mapper也是接口,也没有实现类但最终其方法可以被调用,类比一下,你应该可以猜到Mybatis是如何来实现的。

3.1猜想

我们只定义了接口和方法,在未提供接口实现类的情况下却可以直接对接口方法进行调用。基于Mybatis的这个特性,再结合demo示例中Java反射提供的机制。于是我们猜想:

  1. Mybatis内部应该也是使用了JDK动态代理来执行mapper接口中的方法;
  2. 由于JDK动态代理中需要调用处理器去执行方法逻辑,我们虽然没有为mapper接口编写对应的调用处理器。但是我们为每个mapper接口定义了同名了mapper.xml,而且mapper.xml中的SQL片段其实就是这个方法的执行逻辑。所以Mybatis内部应该做了某些处理,调用处理器的invoke方法执行的应该就是对应的SQL片段:
// 调用处理器 InvocationHandler.invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	// <select id="">
	// <insert id="">
	// <update id="">
	// <delete id="">
}

上面是我们合理的猜想,如果得到印证,那么Mybatis内部使用动态代理的过程也就清晰了。接下来我们通过调试源码进行印证。

3.2源码探究

@SpringBootTest
public class SqlMappingTest {
    @Resource
    AddressMapper addressMapper;
    @Test
    public void test(){
        Address address = addressMapper.selectById(1L);
        System.out.println(address);
    }
}

以查询为例,我们都知道,spring容器为我们注入了一个代理对象addressMapper,代理对象类型为MapperProxy,刚好实现了InvocationHandler接口:

public class MapperProxy<T> implements InvocationHandler, Serializable {
	 @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 (method.isDefault()) {
	       return invokeDefaultMethod(proxy, method, args);
	     }
	   } catch (Throwable t) {
	     throw ExceptionUtil.unwrapThrowable(t);
	   }
	   final MapperMethod mapperMethod = cachedMapperMethod(method);
	   return mapperMethod.execute(sqlSession, args);
	 }
}

再看一下invoke的执行逻辑是否是调用mapper.xml中的SQL片段,以及它是如何进行调用:

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    ...
  }

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        ...
        break;
      }
      case UPDATE: {
        ...
        break;
      }
      case DELETE: {
        ...
        break;
      }
      case SELECT:
        ...
        break;
      case FLUSH:
        ...
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    return result;
  }
}

上述代码中可以看到:
mapper.xml中的SQL片段最终被加载成MapperMethod对象,InvocationHandler.invoke的调用是执行MapperMehtod.execute方法。execute方法就是将SQL分成CRUD几个类型,然后根据当前MapperMehtod的类型进行SQL的执行,显然后面还有SQL参数、结果集的处理等逻辑,但我们的猜想到这里其实就差不多可以被印证了。

扩展:虽然关于Mybatis中未实现的Mapper接口如何被调用执行 的猜想已经被印证,其过程和我们的猜想符合。但在过程中可能会引出其他的思考,比如:

  1. 代理对象是如何创建的;
  2. mapper.xml中的方法如何被转换为MapperMethod对象;
  3. execute方法中,确定SQL类型后,SQL的处理、结果集的处理等等

这些如果有精力的话,后面会陆续展开描述。

梳理与总结

  1. JDK的动态代理是通过实现InvocationHandler接口的形式来实现的。即使接口没有定义实现类,也可以通过JDK动态代理的方式为接口创建代理对象。就接口实现而言,你确实可以把InvocationHandler的这种机制看成是另一种接口实现,所以,既然可以直接去实现接口,为什么还要多此一举去使用InvocationHandler呢?如果你有此疑问,Mybatis给出了很好的答案:接口一定需要显式定义实现类吗?接口和实现可以解耦,定义的接口不再局限于通过接口实现类去实现,可以根据需要以其他方式(mapper.xml)去实现接口;
  2. Mybatis中,MapperProxy通过实现InvocationHandler接口为mapper接口创建代理对象 ;
  3. 根据InvocationHandler的调用机制,对mapper接口中方法的调用,最终都将交给调用处理器的invoke方法,Mybatis在该方法中完成了对mapper.xml SQL方法的实际调用

  • 如有理解错误或不足之处,欢迎大家留言讨论

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/889898.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go开发环境设置-安装与交叉编译(二)

1. 引言 Go语言&#xff0c;又称Golang&#xff0c;是Google开发的一门编程语言&#xff0c;以其高效、简洁和并发编程的优势受到广泛欢迎。作为一门静态类型、编译型语言&#xff0c;Go在构建网络服务器、微服务和命令行工具方面表现突出。 在开发过程中&#xff0c;开发者常…

吸毛效果好的宠物空气净化器分享,希喂、霍尼韦尔、米家实测

说起宠物空气净化器&#xff0c;几年前我可能会一脸鄙夷&#xff1a;为啥要花这种智商税冤枉钱&#xff1f; 直到之前养了一只猫&#xff0c;被家中乱飞的浮毛和滂臭的异味搞到头晕&#xff0c;于是作为i一个养宠的家电测评博主&#xff0c;索性对宠物空气净化器这玩意做了超级…

前端继承:原理、实现方式与应用场景

目录 一、定义 二、语法和实现方式 1.原型链继承 2.构造函数继承 3.组合继承 4.ES6类继承 三、使用方式 四、优点 五、缺点 六、适用场景 一、定义 前端继承是指在面向对象编程中&#xff0c;一个对象可以继承另一个对象的属性和方法。在前端领域&#xff0c;通常是指…

OpenCV高级图形用户界面(1)创建滑动条函数createTrackbar()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 创建一个滑动条并将其附加到指定的窗口。 该函数 createTrackbar 创建一个具有指定名称和范围的滑动条&#xff08;滑块或范围控制&#xff09;…

C语言之扫雷小游戏(完整代码版)

说起扫雷游戏&#xff0c;这应该是很多人童年的回忆吧&#xff0c;中小学电脑课最常玩的必有扫雷游戏&#xff0c;那么大家知道它是如何开发出来的吗&#xff0c;扫雷游戏背后的原理是什么呢&#xff1f;今天就让我们一探究竟&#xff01; 扫雷游戏介绍 如下图&#xff0c;简…

使用3080ti配置安装blip2

使用3080ti运行blip2的案例 本机环境&#xff08;大家主要看GPU&#xff0c;ubuntu版本和cuda版本即可&#xff09;&#xff1a;安装流程我最后安装的所有包的信息&#xff08;python 3.9 &#xff09;以供参考&#xff08;environment.yml&#xff09;&#xff1a; 本机环境&a…

【python实操】python小程序之计算对象个数、游戏更新分数

引言 python小程序之计算对象个数、游戏更新分数 文章目录 引言一、计算对象个数1.1 题目1.2 代码1.3 代码解释1.3.1 代码结构1.3.2 模块解释1.3.3 解释输出 二、游戏更新分数2.1 题目2.2 代码2.3 代码解释2.3.1 定义 Game 类2.3.2 创建 Game 实例并调用方法 三、思考3.1 计算对…

安卓13禁止锁屏 关闭锁屏 android13禁止锁屏 关闭锁屏

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.彩蛋1.前言 设置 =》安全 =》屏幕锁定 =》 无。 我们通过修改系统屏幕锁定配置,来达到设置屏幕不锁屏的配置。像网上好多文章都只写了在哪里改,改什么东西,但是实际上并未写明为什么要改那…

RabbitMQ 高级特性——死信队列

文章目录 前言死信队列什么是死信常见面试题死信队列的概念&#xff1a;死信的来源&#xff08;造成死信的原因有哪些&#xff09;死信队列的应用场景 前言 前面我们学习了为消息和队列设置 TTL 过期时间&#xff0c;这样可以保证消息的积压&#xff0c;那么对于这些过期了的消…

数据结构-4.6.KMP算法(旧版下)-朴素模式匹配算法的优化

一.绪论&#xff1a; 当主串字符和模式串字符不匹配时会执行jnext[j]来改变模式串的指针&#xff0c;但主串的指针不变。 二.求模式串的next数组&#xff1a; 1.例一&#xff1a; 如模式串abcabd&#xff0c;当第六个字符d匹配失败时&#xff0c;此时主串中前五个字符abcab都…

连锁店线下线上一体化收银系统源码

近年来线下线上一体化已经成为很多连锁门店追求的方向。其中&#xff0c;线下门店能够赋予品牌发展的价值依然不可小觑。在线下门店中&#xff0c;收银系统可以说是运营管理的关键工具&#xff0c;好的收银系统能够为品牌门店赋能。对于连锁品牌而言&#xff0c;对收银系统的要…

软媒市场新蓝海:软文媒体自助发布与自助发稿的崛起

在信息时代的浪潮中,软媒市场以其独特的魅力和无限的潜力,成为了企业营销的新宠。随着互联网的飞速发展,软文媒体自助发布平台应运而生,为企业提供了更加高效、便捷的营销方式。而自助发稿功能的加入,更是让软媒市场的蓝海变得更加广阔。 软媒市场的独特价值 软媒市场之所以能…

Android Studio Koala中Kotlin引入序列化Parcelable

找了一堆资料没有新构建序列化的方法&#xff0c;踩坑经历如下&#xff1a; 前提是使用Kotlin创建的项目 之前的build.gradle版本写法如下&#xff1a; 但是新版Android Studio Koala使用序列化模式发生了改变&#xff0c;如下&#xff1a; 测试成功如下&#xff1a; 发出来…

【万字长文】Word2Vec计算详解(三)分层Softmax与负采样

【万字长文】Word2Vec计算详解&#xff08;三&#xff09;分层Softmax与负采样 写在前面 第三部分介绍Word2Vec模型的两种优化方案。 【万字长文】Word2Vec计算详解&#xff08;一&#xff09;CBOW模型 markdown行 9000 【万字长文】Word2Vec计算详解&#xff08;二&#xff0…

PyCharm+ssh跳板机+服务器

PyCharmssh跳板机服务器 文章目录 PyCharmssh跳板机服务器准备工作登录服务器查看CUDA查看conda创建虚拟环境 前言配置ssh免密登录设置ssh隧道配置pycharm测试第一种第二种 传输数据 准备工作 登录服务器 直接ssh连接就行,在终端(命令行)直接输入下面命令: 跳板机&#xff1…

windows系统更新升级node指定版本【避坑篇!!!亲测有效】(附带各版本node下载链接)一定看到最后!不用删旧版!

Node.js 是一个开源、跨平台的 JavaScript 运行时环境&#xff0c;广泛应用于服务器端和网络应用的开发。随着 Node.js 版本的不断更新&#xff0c;我们可能需要升级到特定版本以满足项目需求或修复安全漏洞。又或者是学习开发另外一个新项目&#xff0c;新项目对Node版本要求更…

数学建模算法与应用 第12章 现代优化算法

目录 12.1 粒子群优化算法 Matlab代码示例&#xff1a;粒子群优化算法求解函数最小值 12.2 遗传算法 Matlab代码示例&#xff1a;遗传算法求解函数最小值 12.3 蚁群算法 Matlab代码示例&#xff1a;蚁群算法求解旅行商问题 12.4 Matlab 遗传算法工具 使用遗传算法工具箱…

基于Python+sqlite3实现(Web)图书管理系统

项目名称&#xff1a;LibraryManagementSystem 一、系统目标 使用了Python作为语言,以django为后台&#xff0c;sqlite3作为数据库&#xff0c;UI基于bootstrap的图书管理系统&#xff0c;模拟图书管理的真实场景&#xff0c;考虑客观需求&#xff0c;界面简洁、操作方便&…

Android Studio实现安卓图书管理系统

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 171安卓小说 1.开发环境 android stuido3.6 jak1.8 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.图书列表 3.图书借阅 4.借阅列表 3.系统截图

Go编译为可执行文件

在window下打包成其他系统可运行的文件 1.在window下打包成window下可执行文件 在项目main.go同级目录下&#xff0c;逐条执行以下命令 set CGO_ENABLED0 set GOOSwindows set GOARCHamd64 go build -o main-windows.exe main.go 2.在window下打包成linux 在项目main.go同级目…