当前位置: 首页 > news >正文

MyBatisPlus插件原理

MyBatis作为一款优秀的持久层框架,其插件机制为开发者提供了强大的扩展能力。无论是实现SQL日志打印、分页查询,还是数据脱敏、权限控制,都可以通过插件轻松搞定。

一、MyBatis插件的本质:拦截四大核心接口

MyBatis插件的核心思想是"拦截"——在SQL执行的关键节点插入自定义逻辑。而这些关键节点,正是由MyBatis的四个核心接口及其方法构成。

为什么是这四个接口?

可以把MyBatis执行SQL的过程想象成一条"生产流水线",而这四个接口就是流水线上的四个关键工位,负责SQL执行的全流程:

核心接口作用(类比工厂工位)核心方法
Executor执行器(总指挥):负责SQL执行的整体调度,包括查询、更新、事务管理等query()update()commit()
StatementHandlerSQL处理器(执行者):负责与数据库交互,创建Statement对象、执行SQLprepare()parameterize()query()
ParameterHandler参数处理器:负责给SQL中的占位符设置参数(Java对象→数据库类型)setParameters()
ResultSetHandler结果集处理器:负责将数据库返回的结果集转换为Java对象handleResultSets()

这四个接口覆盖了SQL执行的完整生命周期:从接收请求、处理参数、执行SQL到转换结果。插件只有拦截这些接口的方法,才能在关键节点插入自定义逻辑。

二、动态代理:插件实现的"黑科技"

知道了要拦截哪些接口,接下来的问题是:MyBatis如何实现"拦截"?
答案是JDK动态代理

用生活案例理解动态代理

想象你去租房的场景:

  • 你(调用者)想找房东(目标对象)租房(原方法)
  • 但你通过中介(代理对象)沟通,中介会先帮你筛选房源、议价(拦截逻辑)
  • 最后中介再联系房东完成租房(执行原方法)

在这个过程中,你没有直接接触房东,但中介悄悄在租房流程中加入了额外操作。这就是动态代理的核心思想:不修改原对象的前提下,通过代理对象在方法执行前后插入自定义逻辑

MyBatis中的代理实现

MyBatis插件的工作流程与租房案例高度相似:

  1. 定义拦截逻辑:开发者编写插件类,指定要拦截的接口和方法(比如拦截StatementHandlerprepare()方法)
  2. 创建代理对象:MyBatis启动时,会为四大接口的实现类创建代理对象(通过JDK动态代理)
  3. 执行拦截流程
    • 当MyBatis调用目标方法(如statementHandler.prepare())时,实际调用的是代理对象的方法
    • 代理对象先执行插件中的拦截逻辑(如记录SQL执行开始时间)
    • 再调用原对象的方法,执行真正的SQL操作
    • 最后执行插件中的后续逻辑(如计算SQL执行耗时)

通过这种方式,插件既不会修改MyBatis的核心代码,又能灵活地插入自定义逻辑。

三、实战案例:编写一个SQL执行时间监控插件

我们通过一个实际案例感受插件的工作机制。假设我们需要监控每条SQL的执行时间,步骤如下:

1. 编写插件类(实现Interceptor接口)

@Intercepts({@Signature(type = StatementHandler.class,  // 要拦截的接口method = "prepare",             // 要拦截的方法args = {Connection.class, Integer.class}  // 方法参数)
})
public class SqlExecutionTimePlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 1. 执行拦截逻辑(记录开始时间)long startTime = System.currentTimeMillis();try {// 2. 执行原方法(让MyBatis继续处理SQL)return invocation.proceed();} finally {// 3. 执行后续逻辑(计算耗时)long endTime = System.currentTimeMillis();long cost = endTime - startTime;// 获取当前执行的SQLStatementHandler statementHandler = (StatementHandler) invocation.getTarget();BoundSql boundSql = statementHandler.getBoundSql();String sql = boundSql.getSql();System.out.println("SQL执行耗时:" + cost + "ms,SQL语句:" + sql);}}// 创建代理对象(固定写法)@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}// 设置插件属性(可在mybatis-config.xml中配置)@Overridepublic void setProperties(Properties properties) {// 可读取配置参数,如设置超时阈值等}
}

2. 配置插件(在mybatis-config.xml中注册)

<configuration><plugins><plugin interceptor="com.example.SqlExecutionTimePlugin"><!-- 可配置插件属性 --></plugin></plugins>
</configuration>

3. 执行效果

当MyBatis执行SQL时,插件会自动打印执行时间:

SQL执行耗时:15ms,SQL语句:SELECT * FROM user WHERE id = ?

四、插件开发注意事项

  1. 拦截范围:插件只能拦截四大核心接口的方法,不可自定义其他接口
  2. 性能影响:避免在插件中编写耗时操作,否则会影响SQL执行效率
  3. 顺序问题:多个插件时,执行顺序由配置顺序决定(先配置的先执行)
  4. 兼容性:过度依赖内部实现可能导致升级MyBatis时出现问题

五、总结

MyBatis插件通过"拦截四大核心接口+JDK动态代理"的方式,实现了对SQL执行流程的灵活扩展。其本质是:

  • 以四大接口为切入点,覆盖SQL执行全流程
  • 用动态代理机制在方法执行前后插入自定义逻辑
  • 不侵入核心代码,却能实现强大的扩展功能

插件的核心不是"修改",而是"增强"——在不破坏原有逻辑的前提下,优雅地扩展功能。

http://www.xdnf.cn/news/1276129.html

相关文章:

  • MVCC和日志
  • 音视频学习(五十一):AAC编码器
  • C4.5算法:增益率(Gain Ratio)
  • 嵌入式第二十四课!!linux应用软件编程与文件操作!!!
  • Rust 实战四 | Traui2+Vue3+Rspack 开发桌面应用:通配符掩码计算器
  • JAVA+AI简化开发操作
  • Java集合中的 LinkedList
  • 每日任务day0810:小小勇者成长记之武器精炼
  • node.js 学习笔记3 HTTP
  • Django @login_required实现登陆认证
  • C/C++内存管理函数模板
  • 小明的魔法地图:迷宫探险智慧(记忆性递归)
  • 【0基础3ds Max】主工具栏介绍(下)
  • [激光原理与应用-223]:机械 - 机加厂加工机械需要2D还是3D图?
  • Python设计模式 - 装饰模式
  • 六、RuoYi-Cloud-Plus OSS文件上传配置
  • QT常用控件三
  • Qt—— 下载、工具介绍以及新建项目
  • 从0开始的中后台管理系统-5(userList页面功能实现)
  • 40.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--扩展功能--集成网关--初始化网关
  • Pytorch进阶-timm库-00快速开始
  • (三)全栈(部署)
  • 精准计算Word文档页数的PHP类
  • 数据结构-deque(双端队列)和queue(队列)区别
  • 【npm、yarn、pnpm】特点对比,按需选择
  • Java 后端性能优化实战:从 SQL 到 JVM 调优
  • 分布微服务电商订单系统Rust编码开发[上]
  • 数组练习(一)
  • vuhub drippingblues靶场攻略
  • #4:MinIO分片上传和集群部署