MybatisPlusInterceptor实现无感修改SQL的底层原理(源码)

MybatisPlusInterceptor底层原理(源码)

比较枯燥,且调用链条庞杂,建议结合调试看,或者出bug时再调试

一、原理时序图

二、源码调用栈

三、 核心类关系图 (UML 简图)

聚合(List)
实现
继承
继承
组合(持有)
<<interface>>
InnerInterceptor
+beforeQuery()
<<interface>>
DataPermissionHandler
+getSqlSegment() : Expression
MybatisPlusInterceptor
-List<InnerInterceptor> interceptors
+intercept()
+addInnerInterceptor()
<<abstract>>
JsqlParserSupport
#parserSingle() : String
#processParser()
<<abstract>>
BaseMultiTableInnerInterceptor
#processSelectBody()
#processPlainSelect()
#builderExpression()
DataPermissionInterceptor
-DataPermissionHandler handler
+beforeQuery()
#processSelect()

四、 角色具体

1. InnerInterceptor (接口)
  • 角色岗位说明书
  • 职责:定义了所有 MP 插件(分页、多租户、数据权限)必须遵守的标准。
  • 关键方法
  • beforeQuery(): 查询前执行(改写 SQL 的主要场所)。
  • beforeUpdate(): 更新前执行。
2. MybatisPlusInterceptor (类)
  • 角色医院院长 / 总调度中心
  • 职责 :它是唯一直接对接 MyBatis 原生拦截机制的入口。它不干具体活,只负责维护一个 InnerInterceptor 列表(医生团队),并按顺序分发任务。
  • 关键方法
  • intercept(): MyBatis 的原生拦截点。
  • addInnerInterceptor(): 注册子插件(招聘医生)。
3. JsqlParserSupport (抽象父类)
  • 角色麻醉师 & 翻译官
  • 职责 :负责脏活累活。将原本看不懂的 SQL 字符串解析为结构化的 AST(抽象语法树),并在手术结束后把树还原为字符串。它还负责初步的路由(是 Select 还是 Update?)。
  • 关键方法
  • parserSingle(): 总入口。调用 JSQLParser 解析 SQL,触发处理流程,最后还原 SQL。
  • processParser(): 路由器。判断 SQL 类型(Select/Insert...),分发给对应的处理方法。
4. BaseMultiTableInnerInterceptor (抽象父类)
  • 角色资深主刀医生 (导航专家)
  • 职责 :它知道怎么拆解复杂的 SQL。它能深入到 SQL 的五脏六腑(FROM 子句、JOIN 子句、子查询、CTE),找到所有涉及的 Table
  • 关键方法
  • processSelectBody(): 递归处理 Select 主体。
  • processPlainSelect(): 拆解最基本的 SELECT ... FROM ... 结构。
  • builderExpression(): 手术核心 。遍历解析出的表列表,循环调用子类的 buildTableExpression(或直接调 Handler),并将生成的条件拼装回 AST。
5. DataPermissionInterceptor (具体实现类)
  • 角色数据权限专科医生
  • 职责 :它是 BaseMultiTableInnerInterceptor 的具体实现。它持有"业务顾问"(Handler),负责将 Handler 的策略应用到手术过程中。
  • 关键方法
  • beforeQuery(): 响应院长的号召,启动 parserSingle
  • processSelect(): 重写父类方法,启动 AST 遍历。
  • setRuleHandler(): 装载业务策略。
6. DataPermissionRuleHandler (接口/用户实现)
  • 角色业务顾问 / 智囊团
  • 职责纯业务逻辑。它不关心 SQL 怎么解剖,只关心"对于这张表,你是谁?你要看什么数据?"。
  • 关键方法
  • getSqlSegment(): 输入表名/别名,输出 SQL 条件片段(如 dept_id = 1)。

五、 协同工作全景剧本

现在,当一条 SQL SELECT * FROM sys_user u 发起时,各组件的协同流程如下:

第一阶段:接诊与分发
  1. MyBatis 准备执行 SQL。
  2. MybatisPlusInterceptor (院长) 拦截请求,遍历医生名单。
  3. 发现 DataPermissionInterceptor (专科医生) ,调用其 beforeQuery 方法。
第二阶段:麻醉与解析 (JsqlParserSupport)
  1. DataPermissionInterceptor 内部调用父类 JsqlParserSupport.parserSingle()
  2. JsqlParserSupport 调用工具类,将 SQL 字符串解析为 AST 对象树
  3. JsqlParserSupport 识别出这是 SELECT 语句,回调子类的 processSelect
第三阶段:手术导航 (BaseMultiTableInnerInterceptor)
  1. 逻辑进入 BaseMultiTableInnerInterceptor 的层级(因为继承关系)。
  2. processSelectBody -> processPlainSelect 开始工作。
  3. 它解析出主表 sys_user,别名 u
  4. 它调用 builderExpression,准备开始处理这张表。
第四阶段:专家会诊 (Handler)
  1. builderExpression 暂停,拿起电话打给 DataPermissionRuleHandler (顾问)
  2. 问:"sys_user 表要加什么条件?"
  3. Handler 运行用户代码,返回 u.dept_id = 1
第五阶段:缝合与出院
  1. builderExpression 拿到 u.dept_id = 1,将其通过 AND 连接到 AST 的 WHERE 节点上。
  2. 递归结束,控制权回到 JsqlParserSupport
  3. JsqlParserSupport 调用 toString(),将改写后的 AST 树变回 SQL 字符串。
  4. DataPermissionInterceptor 利用反射,将新 SQL 塞回 MyBatis 的 BoundSql
  5. MybatisPlusInterceptor 放行,手术成功。

相关推荐
星辰徐哥2 小时前
Spring Boot 微服务架构设计与实现
spring boot·后端·微服务
星辰徐哥2 小时前
Spring Boot 数据导入导出与报表生成
spring boot·后端·ui
明夜之约2 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee2 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Micro麦可乐2 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
Jinkxs2 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
毕设源码_郑学姐2 小时前
计算机毕业设计springboot网络相册设计与实现 基于Spring Boot框架的在线相册管理系统开发与应用 Spring Boot驱动的网络影集设计与实践
spring boot·后端·课程设计
辣机小司2 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录
一条小锦吕*2 小时前
基于Spring Boot + 数据可视化 + 协同过滤算法的推荐系统设计与实现(源码+论文+部署全讲解)
spring boot·算法·信息可视化
Jinkxs2 小时前
Prometheus - 监控微服务:Spring Boot 应用指标暴露与监控
spring boot·微服务·prometheus