mybatis自定义插件通过实现interceptor接口并用@intercepts注解拦截executor、statementhandler等四大核心对象的方法,在prepare等关键节点注入逻辑,实现sql日志打印等功能。

MyBatis 自定义插件通过拦截器(Interceptor)机制,在 SQL 执行的关键节点(如 Executor、StatementHandler、ParameterHandler、ResultSetHandler)上插入自定义逻辑,实现 SQL 日志、分页、字段脱敏、SQL 改写等能力。
理解 MyBatis 插件拦截点和签名规则
MyBatis 插件必须实现 org.apache.ibatis.plugin.Interceptor 接口,并用 @Intercepts 注解声明要拦截的目标类与方法。拦截目标必须是 MyBatis 内部四大核心对象之一:
-
Executor:执行 SQL 的顶层调度器(如
query、update) - StatementHandler:负责 SQL 语句的预编译与执行(最常用,可获取/修改原始 SQL)
-
ParameterHandler:处理参数设置(如
setParameters) -
ResultSetHandler:处理结果集映射(如
handleResultSets)
每个 @Signature 必须指定 type(目标类)、method(方法名)、args(参数类型数组),且签名必须与实际方法完全一致,否则不生效。
编写一个 SQL 日志打印插件(以 StatementHandler 为例)
下面是一个拦截 StatementHandler.prepare 方法、打印最终 SQL 和参数的简单插件:
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlPrintPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
// 获取绑定的 BoundSql(含 SQL 和参数映射)
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
// 打印原始 SQL(未替换参数)
System.out.println("[SQL] " + sql);
System.out.println("[PARAM] " + parameterObject);
// 可选:使用 ParameterHandler 渲染带参数的 SQL(需注意类型转换和 null 处理)
if (parameterObject != null) {
Configuration configuration = statementHandler.getConfiguration();
TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();
// 实际中建议用 MetaObject 封装或引入第三方工具(如 JSqlParser)做模拟渲染
}
return invocation.proceed(); // 继续执行原逻辑
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this); // 必须调用,触发代理生成
}
@Override
public void setProperties(Properties properties) {
// 可接收 XML 中配置的属性,如 <property name="showType" value="full"></property>
}
}
在 MyBatis 配置中注册插件
插件需在 SqlSessionFactoryBean(Spring)或 SqlSessionFactoryBuilder 中注册。Spring Boot 示例:
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.addInterceptor(new SqlPrintPlugin());
}
}
或 XML 方式(mybatis-config.xml):
<configuration><plugins><plugin interceptor="com.example.SqlPrintPlugin"><property name="showType" value="simple"></property></plugin></plugins></configuration>
注意事项与常见陷阱
编写插件时需注意以下关键点:
- 拦截
StatementHandler.prepare能拿到最终 SQL,但此时参数尚未绑定到PreparedStatement,想看“真实执行 SQL”需手动模拟参数填充(不推荐生产环境直接拼接,有注入风险) - 若要改写 SQL(如自动添加租户条件),应在
Executor.query/update或StatementHandler.prepare中修改BoundSql,但需返回新BoundSql实例(不可直接修改原对象) - 插件链顺序影响行为:多个插件按注册顺序依次代理,后注册的先执行(类似 Filter 链)
- 避免在插件中抛出未检查异常,否则会中断整个执行流程;建议做好 try-catch 并记录日志
- 不要在插件中持有大对象或连接资源,防止内存泄漏
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











