MybatisPlusInterceptor实现sql拦截器(超详细)
'# MybatisPlusInterceptor实现sql拦截器(超详细)
一、背景与问题
在分布式系统中,SQL拦截器是实现日志记录、安全校验、性能监控等核心功能的关键组件。MyBatis Plus作为主流ORM框架,其提供的MybatisPlusInterceptor提供了强大的SQL拦截能力。本文将深入解析其底层原理,结合真实开发场景,展示如何通过拦截器实现业务需求。
二、基本原理
MyBatis Plus的SQL拦截器基于MyBatis的Interceptor机制实现。其核心原理如下:
- 拦截器注册:通过
@Intercepts注解定义拦截方法 - 动态代理:MyBatis通过动态代理技术拦截SQL执行过程
- 执行流程:拦截器在SQL执行的各个阶段进行干预
- SQL重写:通过
SqlSession对象修改SQL语句
1. 拦截器执行流程图
[Executor]
↓
[Interceptor Chain]
↓
[SQL Interceptor]
↓
[SQL Execution]三、环境准备
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>四、核心实现
1. 基础拦截器实现
@Intercepts({
@Signature(type = Statement.class, method = "execute", args = {String.class}),
@Signature(type = Statement.class, method = "executeQuery", args = {String.class}),
@Signature(type = Statement.class, method = "executeUpdate", args = {String.class})
})
public class SqlLogInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取SQL语句
String sql = (String) invocation.getArgs()[0];
// 记录日志
System.out.println("Executing SQL: " + sql);
// 执行原方法
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可以设置拦截规则
}
}关键代码解释:
@Intercepts注解定义拦截目标类和方法intercept方法实现核心逻辑Plugin.wrap实现动态代理
2. 带参数的拦截器实现
@Intercepts({
@Signature(type = Statement.class, method = "execute", args = {String.class, Object[].class}),
@Signature(type = Statement.class, method = "executeQuery", args = {String.class, Object[].class}),
@Signature(type = Statement.class, method = "executeUpdate", args = {String.class, Object[].class})
})
public class ParamSqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
String sql = (String) invocation.getArgs()[0];
Object[] args = (Object[]) invocation.getArgs()[1];
// 增强逻辑:打印参数
System.out.println("SQL: " + sql);
System.out.println("Parameters: " + Arrays.toString(args));
return invocation.proceed();
}
// 其他方法同上
}3. 优化型拦截器实现
@Intercepts({
@Signature(type = Statement.class, method = "execute", args = {String.class}),
@Signature(type = Statement.class, method = "executeQuery", args = {String.class}),
@Signature(type = Statement.class, method = "executeUpdate", args = {String.class})
})
public class OptimizedSqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
String sql = (String) invocation.getArgs()[0];
// SQL优化:添加查询缓存
if (sql.startsWith("SELECT")) {
sql = "SELECT * FROM cache_table WHERE " + sql.substring(7);
}
// 执行优化后的SQL
return invocation.proceed();
}
// 其他方法同上
}五、完整案例
1. 日志拦截器完整案例
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加日志拦截器
interceptor.addInnerInterceptor(new SqlLogInterceptor());
return interceptor;
}
}2. 完整的SQL执行日志记录
@RestController
public class LogController {
@Autowired
private UserMapper userMapper;
@GetMapping("/users")
public List<User> getUsers() {
return userMapper.selectList(null);
}
}3. 日志输出示例
Executing SQL: SELECT id,name,age FROM user六、源码解析
1. MybatisPlusInterceptor源码结构
public class MybatisPlusInterceptor implements Interceptor {
private List<InnerInterceptor> innerInterceptors = new ArrayList<>();
public void addInnerInterceptor(InnerInterceptor innerInterceptor) {
innerInterceptors.add(innerInterceptor);
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
for (InnerInterceptor interceptor : innerInterceptors) {
interceptor.intercept(invocation);
}
return invocation.proceed();
}
// 其他方法
}关键点:
- 内部拦截器链式执行
- 基于MyBatis的Interceptor接口实现
- 支持动态添加拦截器
七、进阶使用
1. 权限控制拦截器
@Intercepts({
@Signature(type = Statement.class, method = "execute", args = {String.class})
})
public class AuthInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
String sql = (String) invocation.getArgs()[0];
// 权限校验逻辑
if (sql.contains("DELETE")) {
throw new RuntimeException("DELETE操作被禁止");
}
return invocation.proceed();
}
// 其他方法同上
}2. 性能监控拦截器
public class PerformanceInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long duration = System.currentTimeMillis() - start;
if (duration > 1000) {
System.out.println("Slow SQL: " + duration + "ms");
}
return result;
}
// 其他方法同上
}八、性能与工程实践
1. 性能优化策略
- 避免全表扫描:拦截器应避免修改全表查询
- 减少SQL重写:频繁重写SQL可能影响性能
- 缓存机制:对高频SQL进行缓存处理
- 异步日志:避免日志记录影响主流程
2. 安全风险分析
- SQL注入风险:未正确处理参数化查询
- 权限绕过:拦截器逻辑未完善
- 日志泄露:敏感信息可能被记录
3. 索引优化建议
-- 建议为查询字段添加索引
CREATE INDEX idx_name ON user(name);九、常见问题与踩坑
1. 常见错误及解决办法
错误示例:
@Intercepts({@Signature(type = Statement.class, method = "execute", args = {String.class})})
public class MyInterceptor implements Interceptor {
// 未实现intercept方法
}错误原因: 未实现拦截器核心方法
解决办法: 补充intercept方法实现
2. 典型问题分析
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 拦截器未生效 | 未正确注册 | 检查配置类 |
| SQL被修改错误 | 参数处理不当 | 使用Object[]类型参数 |
| 分页失效 | 未处理分页参数 | 使用Page对象作为参数 |
3. 性能优化案例
@Intercepts({
@Signature(type = Statement.class, method = "execute", args = {String.class})
})
public class PerformanceInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
String sql = (String) invocation.getArgs()[0];
// 只对SELECT语句进行监控
if (sql.startsWith("SELECT")) {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long duration = System.currentTimeMillis() - start;
if (duration > 1000) {
System.out.println("Slow SQL: " + duration + "ms");
}
return result;
}
return invocation.proceed();
}
// 其他方法同上
}十、最佳实践
- 统一日志记录:所有SQL操作都应记录日志
- 分场景拦截:不同业务场景使用不同的拦截器
- 参数化处理:避免直接拼接SQL语句
- 安全校验前置:在SQL执行前进行权限校验
- 性能监控:对慢SQL进行预警
- 避免过度拦截:不要修改核心SQL逻辑
十一、总结
MybatisPlusInterceptor作为MyBatis Plus的重要组件,提供了强大的SQL拦截能力。通过深入理解其工作原理,我们能够实现日志记录、安全校验、性能监控等核心功能。在实际开发中,应根据业务需求选择合适的拦截策略,注意性能和安全风险,遵循最佳实践。通过合理使用SQL拦截器,我们可以提升系统可观测性,增强安全性,同时保持代码的可维护性。
在使用过程中,要特别注意拦截器的执行顺序、参数处理以及性能影响。对于复杂业务场景,建议采用分层拦截策略,将不同功能模块分离,以提高代码的可读性和可维护性。
评论已关闭