2.2Mybatis——代理与SQL映射

news2024/10/6 1:57:23

2.2Mybatis——代理与SQL映射

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

“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.coloradmin.cn/o/2190930.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ESP01 AT指令学习

一 、AT指令 测试指令&#xff1a;ATCWMODE? 参数及取值范围 cwmode&#xff08;1-3&#xff09; 查询指令&#xff1a; ATCWMODE&#xff1f; 当前cwmode的取值 3 设置指令&#xff1a; ATCWMODE3 设置当前的cwmode为 3 1、station 模式 连接到其他wifi 2、softA…

【源码+文档】基于SpringBoot+Vue的酒店管理系统

&#x1f6a9;如何选题&#xff1f; 如何选题、让题目的难度在可控范围&#xff0c;以及如何在选题过程以及整个毕设过程中如何与老师沟通&#xff0c;这些问题是需要大家在选题前需要考虑的&#xff0c;具体的方法我会在文末详细为你解答。 &#x1f6ad;如何快速熟悉一个项目…

如何从 Windows 照片库恢复已删除的照片

数据丢失的主要原因之一是人为错误。更糟糕的是&#xff0c;回收站中没有备份或删除的文件。在这种情况下&#xff0c;数据恢复或适用于 Windows 的专用图片恢复工具可以为您提供帮助&#xff0c;因为它们可以帮助恢复已删除的图片。 牢记这一点&#xff0c;我们将讨论从 Wind…

基于SSM的电影院订票系统设计与实现

文未可获取一份本项目的java源码和数据库参考。 开展本课题的意义及工作内容&#xff1a; 1.意义 系统管理也都将通过计算机进行整体智能化操作&#xff0c;对于电影院订票系统系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如电影信息管理、订单管理等&#xff0c;…

04-SpringBootWeb案例(中)

3. 员工管理 完成了部门管理的功能开发之后&#xff0c;我们进入到下一环节员工管理功能的开发。 基于以上原型&#xff0c;我们可以把员工管理功能分为&#xff1a; 分页查询&#xff08;今天完成&#xff09;带条件的分页查询&#xff08;今天完成&#xff09;删除员工&am…

压摆率(Slew Rate)

1. 定义 压摆率&#xff08;Slew Rate&#xff09;也叫转换速率&#xff0c;是指运算放大器输出电压的最大变化速率&#xff0c;单位通常为伏特每微秒&#xff08;V/s&#xff09;。 如果输入信号的变化速率超过运算放大器的压摆率&#xff0c;输出信号将不能即时跟随输入信号…

多线程编程实例

代码&#xff1a; #include<stdio.h> #include<pthread.h> static int run-1; static int retvalue; void *start_routine(void *arg) {int *running arg;printf("child Thread initial over,pass in parameters:%d\n",*running);while(*running){print…

Prometheus与Grafana的完美结合:打造强大的监控与可视化平台

目录 一、Prometheus简介 1.1、Prometheus基本介绍 1.2、Prometheus监控原理 1.3、Prometheus 的局限性 二、部署Prometheus 2.1、使用apt或者yum安装 2.2、基于官方提供的二进制文件安装 2.3、基于docker镜像直接启动或通过docker-compose编排 2.4、基于Operator部署在Kuberne…

使用TensorBoard可视化模型

目录 TensorBoard简介 神经网络模型 可视化 轮次-损失曲线 轮次-准确率曲线 轮次-学习率曲线 迭代-评估准确率曲线 迭代-评估损失曲线 TensorBoard简介 TensorBoard是一款出色的交互式的模型可视化工具。安装TensorFlow时,会自动安装TensorBoard。如图: TensorFlow可…

TI 毫米波雷达——ADC Buffer的交错与非交错采样

TI 毫米波雷达——ADC Buffer的交错与非交错采样 写在前面ADC Buffer 数据存储形式交错采样 (Interleaved mode)1. 4 Rx 通道存储形式2. 3 Rx 通道存储形式3. 2 Rx 通道存储形式 —— RX0/RX1 Enable only4. 1 Rx 通道存储形式 —— RX0 Enable only 非交错采样 (Non-Interleav…

C++:STL常用算法随笔

主要的头文件#include <algorithm> < functional> <numeric> 遍历算法&#xff1a; for_each、transform(搬运容器到另一个容器中 ) void print1(int val) {cout << val <<" "; } for_each (v.begin(),v.end() , print1) 或者用仿…

NVIDIA网卡系列之ConnectX-6 DX规格信息(200G-PCIe 4.0x16-8PF1000VF-2019年发布)

背景 NVIDIA ConnectX-6是最大支持200G的产品&#xff0c;有DX LX等系列。LX一般是25G比较便宜。 核心关键点 200GbpsPCIe 4.0&#xff0c;最大lane: x16 (4.0的lane速 16GT/s * 16 256T/s&#xff0c;所以支持的是200G的网卡用PCIe4.0)QSFPPF&#xff0c;VF数量&#xff1…

Linux 传输层UDP

文章目录 一、再谈端口号查看知名端口号和网站的强关联信息&#xff1f;一个进程是否可以bind多个端口号&#xff1f;一个端口号是否可以被多个进程bind&#xff1f;理解进程和端口的关系&#xff1f; 二、UDP协议协议格式16位源端口号&#xff1a;16位目的端口号&#xff1a;1…

双指针:滑动窗口

题目描述 给定两个字符串 S 和 T&#xff0c;求 S 中包含 T 所有字符的最短连续子字符串的长度&#xff0c;同时要求时间复杂度不得超过 O(n)。 输入输出样例 输入是两个字符串 S 和 T&#xff0c;输出是一个 S 字符串的子串。样例如下&#xff1a; 在这个样例中&#xff0c…

计算机网络:物理层 —— 物理层下的传输媒体

文章目录 传输媒体导向性媒体同轴电缆双绞线光纤光纤分类中心波长光纤规格光纤的优缺点 非导向性媒体ISM 频段无线电波微波激光红外线可见光 传输媒体 传输媒体是计算机网络设备之间的物理通路&#xff0c;也称为传输介质或传输媒介&#xff0c;并不包含在计算机网络体系结构中…

408笔记|随笔记录|自用|2

文章目录 cache和TLB查找总结 接上篇选择题 打开同一个文件共享同一个内存索引结点 cache和TLB cache由SRAM组成&#xff0c;TLB通常由相联存储器组成&#xff0c;可以由SRAM组成。 DRAM需要不断刷新&#xff0c;性能比较低。 TLB缺失可以由软件或者硬件处理&#xff0c;cac…

JC系列CAN通信说明

目录 一、CAN协议二、指令格式三、通信接线3.1、一对一通信3.2、组网通信 四、寄存器定义五、指令说明4、读取电源电压5、读取母线电流6、读取实时速度8、读取实时位置10、读取驱动器温度11、读取电机温度12、读取错误信息32、设定电流33、设定速度35、设定绝对位置37、设定相对…

Java-进阶二

ArrayList的源代码分析&#xff08;扩容原理&#xff09; 1 使用空参构造的集合&#xff0c;在底层创建一个容量为0的数组。2 添加第一个元素时&#xff0c;底层会扩容创建一个容量为10的数组。3 存满时会扩容1.5倍。4 如果一次添加多个元素&#xff0c;1.5倍还放不下&#xff…

机器学习/数据分析--用通俗语言讲解时间序列自回归(AR)模型,并用其预测天气,拟合度98%+

时间序列在回归预测的领域的重要性&#xff0c;不言而喻&#xff0c;在数学建模中使用及其频繁&#xff0c;但是你真的了解ARIMA、AR、MA么&#xff1f;ACF图你会看么&#xff1f;&#xff1f; 时间序列数据如何构造&#xff1f;&#xff1f;&#xff1f;&#xff0c;我打过不少…

提升客户满意度,数字化售后工单系统的实施策略

售后工单系统助企业优化运营&#xff0c;提升客户满意度。涵盖技术支持、维修服务、客户反馈管理等应用场景。ZohoDesk凭高效定制成企业首选。未来趋势包括AI、物联网、AR/VR技术集成&#xff0c;提升服务智能化水平。 一、售后工单系统的重要性 售后工单系统是企业用来管理和…