深入解析MyBatis-Plus拦截器InnerInterceptor的核心方法与应用场景
1. InnerInterceptor为何成为MyBatis-Plus的灵魂组件第一次接触MyBatis-Plus的拦截器机制时我把它想象成咖啡机的过滤器——原始SQL就像咖啡粉而InnerInterceptor就是那层滤网能根据需要调整咖啡的浓度和风味。这个看似简单的接口实际上为开发者提供了对SQL执行全流程的精细控制能力。在真实项目中我遇到过这样的场景某次排查性能问题时发现某个查询接口被恶意调用导致数据库负载激增。通过实现willDoQuery方法我们仅用20行代码就增加了请求频率限制功能成功阻止了类似攻击。这种灵活性和即时生效的特性正是InnerInterceptor最迷人的地方。与原生MyBatis的Interceptor相比MyBatis-Plus的InnerInterceptor有几个显著优势生命周期更精细覆盖了从SQL准备到结果返回的完整流程默认方法更友好采用Java8的default方法设计只需覆盖必要方法与MP生态无缝集成完美适配分页插件、性能分析器等常用组件2. 解密核心方法从理论到实战2.1 查询拦截双雄willDoQuery与beforeQuery去年优化电商平台商品列表接口时我深刻体会到了这对组合的威力。当用户访问非VIP专属商品时willDoQuery方法就像门卫Override public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { if (isVipProductQuery(ms.getId()) !currentUser.isVip()) { log.warn(非VIP用户尝试访问专属商品); return false; // 拦截查询 } return true; }而beforeQuery则像贴心的管家每次查询前自动注入租户IDOverride public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { if (parameter instanceof Map) { ((Map)parameter).put(tenantId, TenantContext.get()); } }2.2 更新拦截黄金搭档willDoUpdate与beforeUpdate在金融系统中我们利用这对方法实现了数据变更审计。有次同事误操作批量更新了用户余额幸亏有willDoUpdate的二次确认机制Override public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) { if (isBalanceUpdate(ms.getId()) ((Map)parameter).get(amount) 10000) { return confirmBySms(); // 大额操作需短信确认 } return true; }而beforeUpdate则自动维护了数据版本和操作人信息Override public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) { if (parameter instanceof BaseEntity) { BaseEntity entity (BaseEntity)parameter; entity.setUpdateTime(LocalDateTime.now()); entity.setUpdater(SecurityUtils.getUserId()); } }3. 高阶应用场景剖析3.1 数据权限控制的三种实现模式在开发ERP系统时我们总结出这些实战方案SQL重写模式通过beforePrepare动态添加WHERE条件Override public void beforePrepare(StatementHandler sh, Connection conn, Integer transactionTimeout) { String sql sh.getBoundSql().getSql(); if (sql.contains(FROM t_order)) { String newSql sql WHERE dept_id IN ( getPermittedDepts() ); resetSql(sh, newSql); // 自定义SQL重写方法 } }参数注入模式适合已使用预编译SQL的场景结果过滤模式在查询返回后通过AOP过滤3.2 SQL优化五连招某次性能调优中我们通过拦截器实现了自动为慢查询添加/* INDEX() */提示将SELECT *替换为具体字段为大结果集查询强制添加分页参数阻止没有WHERE条件的全表更新为批量插入启用rewriteBatchedStatements4. 避坑指南与最佳实践4.1 五个常见陷阱NPE陷阱beforeGetBoundSql中boundSql可能为null循环调用拦截器内调用Mapper方法导致递归事务失效在拦截器中直接提交/回滚连接性能黑洞复杂逻辑放在高频调用的方法中执行顺序多个拦截器间的依赖关系4.2 性能优化三原则轻量级判断在willDoXxx方法中做快速失败检查缓存预热在setProperties阶段加载必要数据延迟处理将非关键操作移到异步线程记得在某次压测中一个未优化的拦截器使TPS下降了40%。后来我们改用ThreadLocal缓存权限数据性能立即恢复到正常水平。这提醒我们拦截器虽强大但要用对地方。