Mybatis 为什么不需要给Mapper接口写实现类,为什么要使用代理而不是硬编码?

文章目录

  • 核心机制概述
  • 源码分析
    • 1. 获取 Mapper 实例
    • 2. 创建 Mapper 代理对象
    • 3. 拦截方法调用 MapperProxy
    • 4. 关联 SQL 并执行
  • 为什么 MyBatis 采用了代理机制,而不是简单地面向流程化的方式?
      • 1. 解耦和灵活性
      • 2. 方法拦截和事务管理
      • 3. 动态代理支持方法级别的 SQL 定义
      • 4. 增强的扩展性
      • 5. 提高代码的可维护性

核心机制概述

MyBatis 是一款流行的持久层框架,其核心特点之一是可以直接通过定义 Mapper 接口与数据库交互,而无需显式地为接口编写实现类。这种特性极大地提高了开发效率。那么,MyBatis 是如何实现这一特性的?
MyBatis 通过动态代理机制,为 Mapper 接口生成代理对象,拦截接口方法的调用,并根据方法签名找到对应的 SQL 映射进行执行,最后将执行结果返回给调用者。这一过程主要依赖以下几个关键组件:
• SqlSession:提供与数据库交互的核心 API,用于执行 SQL、获取映射器等。
• MapperProxy:动态代理类,用于拦截 Mapper 接口方法调用。
• MapperMethod:封装了 Mapper 方法与 SQL 映射之间的关联关系。

源码分析

1. 获取 Mapper 实例

当调用 SqlSession.getMapper(Class type) 方法时,MyBatis 会为指定的 Mapper 接口生成代理对象。

<T> T getMapper(Class<T> type);

DefaultSqlSession 是 SqlSession 的默认实现类,其 getMapper 方法逻辑如下:

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

这里将 type 和当前的 SqlSession 传递给了 Configuration 对象。

2. 创建 Mapper 代理对象

Configuration 中的 getMapper 方法会通过 MapperRegistry 创建代理对象:

    /*** 根据 Mapper 类型和 SqlSession 获取 Mapper 的代理实例** @param type Mapper 接口类型* @param sqlSession SqlSession 实例* @param <T> Mapper 接口类型* @return 返回对应的 Mapper 代理实例* @throws RuntimeException 如果没有找到对应的 Mapper 或获取代理实例时出错*/public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);}}

MapperProxyFactory 是一个工厂类,专门用于生成 MapperProxy 代理对象。

   /*** 创建 MapperProxy 的实例,生成指定 Mapper 接口的动态代理对象* @param sqlSession* @return*/@SuppressWarnings("unchecked")public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);}

3. 拦截方法调用 MapperProxy

生成的代理对象会通过 MapperProxy 拦截 Mapper 接口的方法调用。
MapperProxy 类
MapperProxy 实现了 InvocationHandler 接口,其 invoke 方法会处理所有代理方法调用。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);
}private MapperMethod cachedMapperMethod(Method method) {return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}

• 如果调用的是 Object 的方法(如 toString、equals),直接执行。
• 否则,将方法与参数封装为 MapperMethod,并执行对应的 SQL。

4. 关联 SQL 并执行

MapperMethod 负责将接口方法与 SQL 映射绑定,并执行 SQL。
MapperMethod 的执行逻辑

public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.insert(command.getName(), param);break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.update(command.getName(), param);break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.delete(command.getName(), param);break;}case SELECT: {if (method.returnsVoid()) {sqlSession.select(command.getName(), method.convertArgsToSqlCommandParam(args), null);result = null;} else if (method.returnsMany()) {result = sqlSession.selectList(command.getName(), method.convertArgsToSqlCommandParam(args));} else {result = sqlSession.selectOne(command.getName(), method.convertArgsToSqlCommandParam(args));}break;}default:throw new BindingException("Unknown execution method for: " + command.getName());}return result;
}

这里通过 SqlSession 的 insert、update、delete 和 select 方法与数据库交互。

为什么 MyBatis 采用了代理机制,而不是简单地面向流程化的方式?

在 MyBatis 中,使用代理的方式相比于直接在方法内部获取 namespace 对应的 XML 并解析 SQL,具有几个显著的优点。虽然通过手动解析 SQL 也能实现功能,但代理机制的使用带来了更多的灵活性、简洁性和可维护性。

1. 解耦和灵活性

使用代理的核心优点之一是 解耦。代理机制将 SQL 执行和业务逻辑分离,避免了在每个方法中手动查找和解析 SQL。这样可以确保:
• 接口和实现的分离:你定义的接口方法与数据库操作解耦,接口定义只是一个“约定”,不需要担心 SQL 语句如何查找、解析、执行等细节。
• 灵活性:动态代理允许在运行时确定需要执行的 SQL,而不需要在方法中硬编码 SQL 路径和执行细节。SQL 的解析和执行由 MyBatis 的代理类自动完成,开发者专注于业务逻辑,不必关心数据库操作的实现。
如果不使用代理,而是面向流程化,方法内部需要实现:
• 手动加载 SQL 映射文件(如 XML)。
• 从 XML 文件中根据方法名解析对应的 SQL 语句。
• 绑定 SQL 的参数并执行。
• 处理结果集并返回。
这样做会导致每个方法的逻辑变得复杂、重复,降低了代码的可读性和可维护性。

2. 方法拦截和事务管理

使用代理时,MyBatis 可以利用代理对象来拦截方法调用,这样就能:
• 统一处理日志、事务、缓存等跨切面功能。例如,事务管理和 SQL 执行的处理都可以统一通过代理类来完成,不需要在每个方法内部显式地调用事务控制逻辑。
• 统一的错误处理:代理机制可以在方法调用时自动捕获异常并进行统一的处理,如 SQL 异常的转换、事务的回滚等。
这种集中式的管理方式,比在方法内部手动管理事务和错误处理更加简洁、可靠和一致。

3. 动态代理支持方法级别的 SQL 定义

MyBatis 的代理机制通过注解或 XML 配置能够动态地将方法名和 SQL 语句绑定起来。代理对象能在方法调用时,动态查找映射文件中对应的 SQL 语句,并通过传入的参数执行 SQL。
• 例如,接口 UserDao 中的 findUserById(int id) 方法,会在代理类中被拦截,动态解析出对应的 SQL 语句并执行。这种方式的好处是,你不需要在每个方法内部去查找 SQL 映射,MyBatis 会自动根据方法名(或注解)定位对应的 SQL,从而使得代码更加简洁和规范。
如果不使用代理,方法内部就需要自己编写代码来:
• 查找与方法名对应的 SQL。
• 解析 SQL 文件。
• 绑定参数并执行查询。
这种手动的方式不仅增加了冗余代码,还会让每个方法的实现变得更复杂。

4. 增强的扩展性

代理机制还为 MyBatis 提供了很强的扩展性。例如:
• 插件机制:你可以通过自定义 MyBatis 插件来扩展或修改 SQL 执行过程,如日志打印、性能监控、缓存等功能。这些都可以通过代理类来实现,而不需要修改每个方法的实现。
• 跨切面功能:代理对象使得 MyBatis 能够轻松地为方法调用提供横向的功能(例如,缓存、事务、权限检查等),而这些功能在流程化的实现中会变得难以统一处理。

5. 提高代码的可维护性

由于代理机制将所有的 SQL 执行和参数绑定逻辑封装在 MapperProxy 中,开发者只需要关注业务逻辑的实现。修改 SQL、改变数据库连接等都可以在配置层完成,不需要修改业务层的代码。
如果采用面向流程的方式,每次修改 SQL 时都需要修改每个方法的实现,导致代码的维护变得复杂,且容易出错。

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

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

相关文章

DevOps流程CICD之Jenkins使用操作

一、jenkins的docker-compose安装部署 请参考 jenkins的docker安装部署配置全网最详细教程-CSDN博客 二、创建repository 三、创建ssh 四、创建视图 五、创建任务 六、配置gitlab钩子 七、自动构建部署CI/CD验证

Solidworks打开无法获得许可,提示(-15,10,10061)错误解决办法

参考文章&#xff1a; https://blog.csdn.net/2301_81263647/article/details/140904773

四、AI知识(其他算法)

四、AI知识&#xff08;其他算法&#xff09; 1.其他算法 终身学习 元学习 2.建模预处理与评估 数据清洗 数据规约 空缺值 噪声数据 数据变换 数据规范化&#xff08;如正则化、归一化&#xff09; 数据压缩 数据规约 数值数据离散化/分类数据概念分层 模型评估 …

【云原生】Docker Compose 从入门到实战使用详解

目录 一、前言 二、Docker Compose 介绍 2.1 Docker Compose概述 2.2 Docker Compose特点 2.3 Docker Compose使用场景 三、Docker Compose 安装 3.1 安装docker环境 3.2 Docker Compose安装方式一 3.2.1 下载最新版 3.2.2 设置权限 3.2.3 设置软链接 3.2.4 查看版本…

wangEditor/editor自定义粘贴后续

背景 按照上一篇文章处理自定义粘贴之后&#xff0c;发现复制表格之后&#xff0c;会出现表格样式失效问题&#xff0c;原因是自定义粘贴没有处理表格数据&#xff0c;导致按照文本格式粘贴了 处理方式 自定义表格&#xff08;如果业务有需求需要更新表格样式的&#xff0c;…

【OTA】论文笔记--《智能网联汽车整车OTA功能设计研究》智能网联汽车OTA系统设计分析报告

智能网联汽车OTA系统设计分析报告 引言 随着汽车智能化、网联化水平不断提升,现代汽车中电子控制单元(ECU)的数量和复杂度持续增加。据统计,高级轿车上电子电气元件的成本已占整车开发成本的60%~70%。为了实现对这些电控单元的软件开发调试、数据标定、文件更新和故障修复,…

【电路理论四】正弦电流电路

正弦电流 正弦量是随时间按正弦规律变动的电路变量。 随时间按正弦规律变动的电流称为正弦电流。 正弦电流的瞬时值表达式&#xff1a; 称为正弦电流的三要素。 分别为振幅/幅值&#xff0c;角频率&#xff0c;初相。 幅值为正弦电流的最大值&#xff0c;恒为正。 为正弦电…

解决Windows无法同时使用有线网和无线网WIFI的问题

参考资料 电脑无线网wifi和有线网同时使用&#xff08;内网外网同时使用&#xff09;用route命令解决Wifi和网卡不能同时上内外网问题 解决方法 对于Windows系统同时连接有线网和无线网Wifi时&#xff0c;只会有一个网关生效&#xff0c;因此我们需要手动通过route命令设置网…

FOC控制原理-ADC采样时机

0、文章推荐 SimpleFOC移植STM32&#xff08;五&#xff09;—— 电流采样及其变换_极对数对电流采样的影响-CSDN博客 FOC 电流采样方案对比&#xff08;单电阻/双电阻/三电阻&#xff09; - 知乎 (zhihu.com) FOC中的三种电流采样方式&#xff0c;你真的会选择吗&#xff1f;…

uniapp:编译微信、h5都正常的,编译钉钉小程序无法找到页面

我们在做uniapp的开发的时候遇到很多莫名其妙的问题&#xff0c;其中就有个很奇葩的问题&#xff0c;我们开发的过程中使用微信小程序/h5调试都是正常编译正常跳转&#xff0c;然后在要上架的时候&#xff0c;编译钉钉小程序成功&#xff0c;但是访问页面无法访问&#xff0c; …

zentao ubuntu上安装

#下载ZenTaoPMS-21.2-zbox_amd64.tar.gz&#xff08;https://www.zentao.net/downloads.html&#xff09; https://dl.zentao.net/zentao/21.2/ZenTaoPMS-21.2-zbox_amd64.tar.gzcd /opt tar -zxvf ZenTaoPMS-21.2-zbox_amd64.tar.gz#启动 /opt/zbox/zbox start /opt/zbox/zbox…

REDIS2.0

string list hash set 无序集合 声明一个key&#xff0c;键里面的值是元素&#xff0c;元素的类型是string 元素的值是唯一的&#xff0c;不能重复 多个集合类型之间可以进行并集&#xff0c;交集&#xff0c;集查的运算 sadd test1 a b c c d &#xff1a;添加5个元素&am…

【论文阅读】DebSDF:深入研究神经室内场景重建的细节和偏差

【论文阅读】DebSDF&#xff1a;深入研究神经室内场景重建的细节和偏差 Abstract3.METHOD3.1 Preliminaries3.2 Uncertainty Guided Prior Filtering3.3 Uncertainty-Guided Ray Sampling3.4 Uncertainty-Guided Smooth Regularization3.5 Bias-aware SDF to Density Transform…

golang,多个proxy拉包的处理逻辑

在Go语言中&#xff0c;当你设置了多个代理&#xff08;GOPROXY&#xff09;时&#xff0c;Go工具链会按照你设置的顺序尝试每个代理。如果第一个代理失败&#xff0c;它会尝试下一个代理&#xff0c;直到成功获取到模块或者所有代理都尝试失败。最后&#xff0c;如果所有代理都…

微服务SpringCloud分布式事务之Seata

视频教程&#xff1a;https://www.bilibili.com/video/BV16P63Y3ESq 效果演示 准备的微服务项目调用的链路如下&#xff1a; 文字描述&#xff1a; gateway模块接收到请求&#xff0c;并发送到order订单模块order订单模块接收到请求&#xff0c;新增一个订单数据后发送一个…

Pyhton知识分享-利用KNN算法实现手写数字识别

利用KNN算法实现手写数字识别 MNIST手写数字识别 是计算机视觉领域中 "hello world"级别的数据集 1999年发布&#xff0c;成为分类算法基准测试的基础随着新的机器学习技术的出现&#xff0c;MNIST仍然是研究人员和学习者的可靠资源。 本次案例中&#xff0c;我们的…

多点通信、流式域套接字

一、广播 1.1广播的发送端模型&#xff1a; #include<myhead.h>#define BEN_IP "192.168.191.129" #define BEN_PORT 8888#define PORT 6666int main(int argc, const char *argv[]) {int oldfd socket(AF_INET,SOCK_DGRAM,0);if(oldfd -1){perror("soc…

AMBA-APB

目录 1.APB 协议 2.APB信号列表 3.数据传输 3.1写传输&#xff08;2种&#xff09; 3.1.1 无等待状态的写传输 3.1.2有等待状态的写传输 3.2写选通信号 (PSTRB) 字节通道映射 3.3读传输&#xff08;2种&#xff09; 3.3.1 无等待状态的读传输 3.3.2有等待状态的读传…

linux自动化一键批量检查主机端口

1、准备 我们可以使用下面命令关闭一个端口 sudo iptables -A INPUT -p tcp --dport 端口号 -j DROP我关闭的是22端口&#xff0c;各位可以关其它的或者打开其它端口测试&#xff0c;谨慎关闭22端口&#xff01;不然就会像我下面一样握手超时&#x1f62d;&#x1f62d;&…

0055. shell命令--useradd

目录 55. shell命令--useradd 功能说明 语法格式 选项说明 选项 退出值 相关文件 /etc/passwd /etc/shadow /etc/group /etc/gshadow /etc/skel/ /etc/login.defs /etc/default/useradd 实践操作 注意事项 55. shell命令--useradd 功能说明 useradd 命令是 Lin…