MyBatis 是 Java 领域广泛使用的 ORM 框架,而拦截器(Interceptor)机制是其强大的扩展能力之一。本文将带你系统了解 MyBatis 拦截器的使用方法、支持拦截的类型以及实际应用案例。
一、什么是 MyBatis 拦截器?
MyBatis 拦截器(Interceptor)是通过 Java 的插件机制(基于动态代理)实现的一种 增强功能。它允许开发者在 MyBatis 的核心执行流程中“插入”自定义逻辑,例如 SQL 执行前后、参数处理、结果映射等阶段。
MyBatis 使用 @Intercepts
注解标识拦截点,通过实现 org.apache.ibatis.plugin.Interceptor
接口来自定义插件。
二、拦截器的使用方式
2.1 实现 Interceptor 接口
@Intercepts({
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}
)
})
public class MyExampleInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 在这里编写增强逻辑,比如打印 SQL
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可以读取配置文件中的属性
}
}
2.2 注册插件
可以在 MyBatis 配置文件中注册插件:
<plugins>
<plugin interceptor="com.example.MyExampleInterceptor">
<property name="someProperty" value="value"/>
</plugin>
</plugins>
或者通过 Spring Boot 配置类注册:
@Configuration
@MapperScan("com.example.mapper")
public class MybatisConfig {
@Bean
public Interceptor myInterceptor() {
return new MyExampleInterceptor();
}
}
三、MyBatis 支持的拦截点
MyBatis 拦截器只能拦截以下四种接口中的方法:
接口 | 可拦截方法 | 说明 |
---|---|---|
Executor | update 、query | 执行增删改查操作 |
ParameterHandler | getParameterObject 、setParameters | 参数处理 |
ResultSetHandler | handleResultSets | 结果集处理 |
StatementHandler | prepare 、parameterize 、batch 、update 、query | SQL 处理 |
**注意:**只能拦截这四个接口的方法,其他类无法拦截。
四、MyBatis 拦截器可以实现什么?
MyBatis 拦截器强大之处在于它的灵活性和侵入性低,常见的应用包括:
4.1 分页逻辑处理(如 PageHelper)
拦截 StatementHandler.prepare
方法,修改 SQL 增加分页语句(如 LIMIT
)。
BoundSql boundSql = statementHandler.getBoundSql();
String originalSql = boundSql.getSql();
String pageSql = originalSql + " LIMIT 0,10";
Field sqlField = boundSql.getClass().getDeclaredField("sql");
sqlField.setAccessible(true);
sqlField.set(boundSql, pageSql);
4.2 SQL 日志打印
拦截 StatementHandler.prepare
,输出最终执行的 SQL 与参数。
BoundSql boundSql = statementHandler.getBoundSql();
System.out.println("SQL: " + boundSql.getSql());
System.out.println("Params: " + boundSql.getParameterObject());
4.3 多租户隔离
根据租户 ID 重写 SQL,在 WHERE 语句中追加租户过滤条件。
String tenantId = TenantContext.getTenantId();
String originalSql = boundSql.getSql();
String newSql = originalSql + " AND tenant_id = '" + tenantId + "'";
sqlField.set(boundSql, newSql);
4.4 数据权限控制
动态分析用户角色,改写 SQL 实现字段或行级权限隔离。
if (!UserContext.isAdmin()) {
String originalSql = boundSql.getSql();
String newSql = originalSql + " AND created_by = '" + UserContext.getUserId() + "'";
sqlField.set(boundSql, newSql);
}
4.5 动态表名、分表分库
根据 ThreadLocal 中上下文信息修改表名。
String logicTable = "user";
String realTable = logicTable + "_" + ShardContext.getShardSuffix();
String newSql = originalSql.replaceAll("\\buser\\b", realTable);
sqlField.set(boundSql, newSql);
4.6 SQL 审计 / 灰度发布控制
记录操作行为日志,或根据不同灰度策略决定是否执行或修改 SQL。
AuditLogger.log(boundSql.getSql(), boundSql.getParameterObject());
if (GrayReleaseContext.isBlocked(boundSql.getSql())) {
throw new RuntimeException("该 SQL 被灰度策略阻止执行");
}
五、小结
MyBatis 拦截器是一个轻量却强大的扩展机制,适用于各种中间件能力的实现,如分页、多租户、安全控制等。但要注意其侵入性操作可能引发潜在性能问题或逻辑异常,务必在使用时做好单元测试与边界验证。
如果你正在使用 MyBatis 进行复杂的查询处理或框架级能力封装,拦截器无疑是一个值得深入掌握的利器。
你在项目中用过拦截器做哪些扩展?欢迎评论区分享你的经验!