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