MybatisPlusInterceptor实现sql拦截器(超详细)

'# MybatisPlusInterceptor实现sql拦截器(超详细)

一、背景与问题

在分布式系统中,SQL拦截器是实现日志记录、安全校验、性能监控等核心功能的关键组件。MyBatis Plus作为主流ORM框架,其提供的MybatisPlusInterceptor提供了强大的SQL拦截能力。本文将深入解析其底层原理,结合真实开发场景,展示如何通过拦截器实现业务需求。

二、基本原理

MyBatis Plus的SQL拦截器基于MyBatis的Interceptor机制实现。其核心原理如下:

  1. 拦截器注册:通过@Intercepts注解定义拦截方法
  2. 动态代理:MyBatis通过动态代理技术拦截SQL执行过程
  3. 执行流程:拦截器在SQL执行的各个阶段进行干预
  4. 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. 性能优化策略

  1. 避免全表扫描:拦截器应避免修改全表查询
  2. 减少SQL重写:频繁重写SQL可能影响性能
  3. 缓存机制:对高频SQL进行缓存处理
  4. 异步日志:避免日志记录影响主流程

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();
    }
    
    // 其他方法同上
}

十、最佳实践

  1. 统一日志记录:所有SQL操作都应记录日志
  2. 分场景拦截:不同业务场景使用不同的拦截器
  3. 参数化处理:避免直接拼接SQL语句
  4. 安全校验前置:在SQL执行前进行权限校验
  5. 性能监控:对慢SQL进行预警
  6. 避免过度拦截:不要修改核心SQL逻辑

十一、总结

MybatisPlusInterceptor作为MyBatis Plus的重要组件,提供了强大的SQL拦截能力。通过深入理解其工作原理,我们能够实现日志记录、安全校验、性能监控等核心功能。在实际开发中,应根据业务需求选择合适的拦截策略,注意性能和安全风险,遵循最佳实践。通过合理使用SQL拦截器,我们可以提升系统可观测性,增强安全性,同时保持代码的可维护性。

在使用过程中,要特别注意拦截器的执行顺序、参数处理以及性能影响。对于复杂业务场景,建议采用分层拦截策略,将不同功能模块分离,以提高代码的可读性和可维护性。

sql
最后修改于:2026年09月28日 07:36

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日