Mybatis Plus 拦截器忽略机制全解:InterceptorIgnoreHelper 源码与实战
Mybatis Plus 的 InterceptorIgnoreHelper 深度解析 —— 拦截器忽略策略的实现与应用场景
在企业级 Java 开发中,MyBatis-Plus 以其强大的插件机制和极简的 API,成为了 MyBatis 用户的首选增强工具。插件机制是 MyBatis-Plus 的核心亮点之一,极大地提升了开发效率和系统的可维护性。本文将围绕 MyBatis-Plus 插件体系、InnerInterceptor
接口、官方常用插件、以及如何通过 InterceptorIgnoreHelper
灵活控制插件行为进行深入解析。
一、MyBatis-Plus 插件体系概述
MyBatis-Plus 通过插件机制为开发者提供了丰富的功能扩展。所有 MyBatis-Plus 官方插件都实现了 InnerInterceptor
接口,该接口定义了插件的基本行为和生命周期方法。开发者既可以直接使用官方插件,也可以基于该接口自定义扩展,满足个性化需求。
主要插件一览
目前,MyBatis-Plus 官方内置了以下常用插件:
-
自动分页插件(PaginationInnerInterceptor)
自动识别数据库类型,实现高效的物理分页,无需手写分页 SQL,极大简化分页开发。 -
多租户插件(TenantLineInnerInterceptor)
支持多租户场景,通过自动拼接租户条件,实现数据隔离,保障不同租户间数据安全。 -
动态表名插件(DynamicTableNameInnerInterceptor)
支持根据业务需求动态切换表名,常用于分表、历史归档等场景。 -
乐观锁插件(OptimisticLockerInnerInterceptor)
实现基于版本号的乐观锁机制,防止并发写入时的数据覆盖,提升数据一致性。 -
SQL 性能规范插件(IllegalSQLInnerInterceptor)
检查 SQL 合规性,防止出现不规范或高风险的 SQL 语句,提升系统安全性和稳定性。 -
防止全表更新与删除插件(BlockAttackInnerInterceptor)
拦截无 WHERE 条件的 UPDATE 或 DELETE 操作,防止误操作导致全表数据被修改或删除。
通过这些插件,MyBatis-Plus 能够帮助开发者在不侵入业务代码的前提下,轻松实现分页、多租户、数据安全等常见需求,大大提升了开发效率和系统健壮性。
二、InnerInterceptor 接口简介
InnerInterceptor
是 MyBatis-Plus 插件体系的核心接口。所有插件都需实现该接口,统一了插件的扩展点和行为规范。其主要方法包括:
beforeQuery
:查询前置处理beforeUpdate
:更新前置处理beforePrepare
:SQL 预编译前处理willDoQuery
、willDoUpdate
:是否执行查询/更新setProperties
:插件参数设置
通过实现这些方法,插件可以在 SQL 执行的各个阶段进行拦截、增强或校验。
三、为什么需要 InterceptorIgnoreHelper?
虽然插件机制极大地方便了开发,但在实际业务中,并不是所有 SQL 都需要被所有插件拦截。例如:
- 某些统计报表 SQL 不需要多租户隔离
- 某些特殊批量操作需要临时关闭 SQL 防护
- 某些历史表查询无需动态表名处理
如果没有灵活的“忽略机制”,开发者只能通过复杂的条件判断或拆分代码来规避插件影响,既繁琐又容易出错。
四、InterceptorIgnoreHelper 的原理与实现
1. 注解驱动
MyBatis-Plus 提供了 @InterceptorIgnore
注解,可以直接标注在 Mapper 类或方法上,声明需要忽略的拦截器类型。例如:
@InterceptorIgnore(tenantLine = "true", blockAttack = "true")
List<User> selectAllUser();
这样,当前方法在执行时就会自动跳过多租户和防全表更新插件。
2. 策略缓存
InterceptorIgnoreHelper
通过静态 Map 和 ThreadLocal 缓存忽略策略,提升性能并支持线程隔离。
- 全局缓存:
IGNORE_STRATEGY_CACHE
,存储每个 Mapper 或方法的忽略策略。 - 线程本地缓存:
IGNORE_STRATEGY_LOCAL
,支持在代码中临时手动设置忽略策略,优先级高于注解。
3. 忽略策略判定
每个拦截器在执行前都会调用 InterceptorIgnoreHelper
的相关方法(如 willIgnoreTenantLine
),判断当前 SQL 是否需要跳过对应拦截逻辑。
4. 手动控制
有时需要在代码中临时设置忽略策略,可以通过:
// 请尽量使用 try finally 的方式来保证能正确得到关闭
try {// 设置忽略租户插件InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());// 执行逻辑 ..
} finally {// 关闭忽略策略InterceptorIgnoreHelper.clearIgnoreStrategy();
}
五、源码解读
1. 缓存初始化
-
类级别注解缓存
initSqlParserInfoCache(Class<?> mapperClass)
读取 Mapper 类上的@InterceptorIgnore
注解,构建并缓存策略。 -
方法级别注解缓存
initSqlParserInfoCache(IgnoreStrategy mapperAnnotation, String mapperClassName, Method method)
读取方法上的注解,优先级高于类级别。
2. 忽略判定方法
willIgnoreTenantLine(String id)
willIgnoreBlockAttack(String id)
willIgnoreDynamicTableName(String id)
willIgnoreIllegalSql(String id)
willIgnoreDataPermission(String id)
这些方法内部最终都会调用通用的 willIgnore(String id, Function<IgnoreStrategy, Boolean> function)
,根据缓存和当前线程策略判断是否忽略。
六、常用方法说明与代码示例
InterceptorIgnoreHelper
提供了多种便捷方法,帮助开发者灵活控制插件的忽略行为。以下是常用方法的详细说明及代码示例:
1. handle(IgnoreStrategy ignoreStrategy)
作用:
手动设置当前线程的拦截器忽略策略,优先级高于注解。适用于需要在代码中临时关闭某些插件的场景。
示例:
// 临时忽略多租户插件
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build()
);
// 执行业务代码,此时多租户插件不会生效
userMapper.selectList(null);
// 记得清理,避免影响后续操作
InterceptorIgnoreHelper.clearIgnoreStrategy();
2. clearIgnoreStrategy()
作用:
清除当前线程的忽略策略,恢复默认拦截行为。通常与 handle
配对使用。
示例:
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().blockAttack(true).build()
);
// ... 执行需要忽略防全表更新的操作
InterceptorIgnoreHelper.clearIgnoreStrategy();
3. initSqlParserInfoCache(Class<?> mapperClass)
作用:
初始化并缓存 Mapper 类上的 @InterceptorIgnore
注解信息。
示例:
// 一般由框架自动调用,开发者无需手动调用
InterceptorIgnoreHelper.initSqlParserInfoCache(UserMapper.class);
4. initSqlParserInfoCache(IgnoreStrategy mapperAnnotation, String mapperClassName, Method method)
作用:
初始化并缓存 Mapper 方法上的 @InterceptorIgnore
注解信息,优先级高于类上的注解。
示例:
// 一般由框架自动调用,开发者无需手动调用
Method method = UserMapper.class.getMethod("selectAllUser");
InterceptorIgnoreHelper.initSqlParserInfoCache(null, UserMapper.class.getName(), method);
5. willIgnoreTenantLine(String id)
作用:
判断当前 SQL 是否需要忽略多租户插件。
示例:
if (InterceptorIgnoreHelper.willIgnoreTenantLine("com.example.mapper.UserMapper.selectAllUser")) {// 当前 SQL 会忽略多租户插件
}
6. willIgnoreDynamicTableName(String id)
作用:
判断当前 SQL 是否需要忽略动态表名插件。
示例:
if (InterceptorIgnoreHelper.willIgnoreDynamicTableName("com.example.mapper.UserMapper.selectAllUser")) {// 当前 SQL 会忽略动态表名插件
}
7. willIgnoreBlockAttack(String id)
作用:
判断当前 SQL 是否需要忽略防全表更新与删除插件。
示例:
if (InterceptorIgnoreHelper.willIgnoreBlockAttack("com.example.mapper.UserMapper.deleteAll")) {// 当前 SQL 会忽略防全表更新与删除插件
}
8. willIgnoreIllegalSql(String id)
作用:
判断当前 SQL 是否需要忽略 SQL 性能规范插件。
示例:
if (InterceptorIgnoreHelper.willIgnoreIllegalSql("com.example.mapper.UserMapper.selectAllUser")) {// 当前 SQL 会忽略 SQL 性能规范插件
}
9. willIgnoreDataPermission(String id)
作用:
判断当前 SQL 是否需要忽略数据权限插件。
示例:
if (InterceptorIgnoreHelper.willIgnoreDataPermission("com.example.mapper.UserMapper.selectAllUser")) {// 当前 SQL 会忽略数据权限插件
}
10. willIgnoreOthersByKey(String id, String key)
作用:
判断当前 SQL 是否需要忽略自定义的其他插件。
示例:
if (InterceptorIgnoreHelper.willIgnoreOthersByKey("com.example.mapper.UserMapper.selectAllUser", "customPlugin")) {// 当前 SQL 会忽略自定义插件 customPlugin
}
11. willIgnore(String id, Function<IgnoreStrategy, Boolean> function)
作用:
通用的忽略判定方法,支持自定义判断逻辑。
示例:
boolean ignore = InterceptorIgnoreHelper.willIgnore("com.example.mapper.UserMapper.selectAllUser",IgnoreStrategy::getTenantLine
);
if (ignore) {// 当前 SQL 会忽略多租户插件
}
七、实际应用场景
-
部分 SQL 不需要多租户隔离
某些统计报表、全局查询等场景下,可以通过注解或手动设置跳过多租户插件。 -
临时关闭 SQL 攻击防护
某些特殊批量操作需要绕过 BlockAttackInterceptor,可通过@InterceptorIgnore(blockAttack = "true")
实现。 -
数据权限灵活控制
对于部分无需数据权限校验的接口,可通过注解声明忽略。 -
动态表名插件的灵活开关
某些历史表、归档表查询时,不需要动态表名处理,也可通过该机制关闭。
八、注意事项
- 优先级:方法上的注解优先于类上的注解,手动设置(
ThreadLocal
)优先级最高。 - 记得清理:手动设置忽略策略后,务必调用
clearIgnoreStrategy()
,避免影响后续线程操作。 - 参数值:注解参数支持
"true"
、"false"
、"on"
、"off"
、"1"
、"0"
等多种写法。
九、总结
InterceptorIgnoreHelper
是 MyBatis-Plus 插件体系中非常实用的“调度员”,让我们可以灵活、细粒度地控制各类拦截器的生效范围。无论是通过注解还是手动代码控制,都极大提升了业务开发的灵活性和可维护性。
建议在实际开发中,合理利用该机制,既保证了插件的安全性,也兼顾了特殊业务场景的灵活需求。
参考文献
Mybatis Plus 官网 - 插件主体