1、简述
MyBatis 除了强大的 ORM 功能,还提供了 插件(Interceptor)机制,允许我们在执行 SQL 的生命周期中进行拦截和增强。
常见的应用场景有:
🔹 SQL 日志打印和性能监控
🔹 数据权限控制
🔹 分页拦截
🔹 参数加解密
本文将详细介绍 MyBatis 插件的原理、编写方式,并通过实践样例展示如何实现 SQL 执行耗时监控插件。
2、MyBatis 插件的原理
MyBatis 通过 四大对象的动态代理来实现插件拦截:
🔹 Executor
—— SQL 执行器
🔹 StatementHandler
—— 处理 SQL 语句
🔹 ParameterHandler
—— 处理参数
🔹 ResultSetHandler
—— 处理结果集
我们可以通过 @Intercepts
和 @Signature
注解指定要拦截的对象和方法。
3、编写 MyBatis 插件的步骤
🔹 实现 org.apache.ibatis.plugin.Interceptor
接口
🔹 在 @Intercepts
注解中声明拦截点
🔹 实现 intercept
方法,编写增强逻辑
🔹 在 MyBatis 配置文件或 Spring Boot 配置类中注册插件
4、实践样例
4.1 自定义插件类
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlCostInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
// 执行原方法
Object result = invocation.proceed();
long end = System.currentTimeMillis();
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
Statement statement = (Statement) invocation.getArgs()[0];
System.out.println("[MyBatis SQL Monitor] SQL: "
+ statementHandler.getBoundSql().getSql()
+ " | Cost: " + (end - start) + " ms");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可通过配置传入参数
}
}
4.2 在 MyBatis 中注册插件
方式一:mybatis-config.xml
<plugins>
<plugin interceptor="com.example.mybatis.SqlCostInterceptor"/>
</plugins>
方式二:Spring Boot 配置类
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(javax.sql.DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
// 注册插件
SqlCostInterceptor interceptor = new SqlCostInterceptor();
factoryBean.setPlugins(interceptor);
return factoryBean.getObject();
}
}
4.3 测试代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private UserMapper userMapper;
@GetMapping("/test")
public Object test() {
return userMapper.selectUserById(1);
}
}
4.4 输出结果
运行后控制台日志:
[MyBatis SQL Monitor] SQL: SELECT id, username FROM users WHERE id = ? | Cost: 15 ms
5、总结
🔹 拦截原理:MyBatis 使用动态代理,在四大对象的执行方法上插入自定义逻辑。
🔹 插件结构:实现 Interceptor
→ 定义 @Intercepts
→ 注册插件。
🔹 适用场景:日志监控、权限校验、分页插件、SQL 审计、数据加解密等。
🔹 注意事项: 插件过多可能影响性能,避免在插件里写复杂业务逻辑,合理选择拦截点(Executor / StatementHandler 等)