应精准捕获UserNotFoundException等自定义业务异常、NullPointerException及MyBatis的BindingException/TooManyResultsException三类有业务语义的异常,通过@ControllerAdvice统一处理,写入短TTL空标记并联动布隆过滤器与监控。

在缓存穿透场景下,捕获底层异常不是为了“兜底报错”,而是要识别出那些明确指向“数据根本不存在”的信号,从而触发空值缓存、布隆过滤器更新等防护动作。关键不在捕获多少异常,而在精准识别有业务语义的异常类型,并在不拖慢响应的前提下完成防御闭环。
聚焦三类高价值穿透相关异常
不是所有异常都值得拦截——应只响应那些能可靠反映“查无此数据”的运行时异常:
- UserNotFoundException、ProductNotExistException 等自定义业务异常:这是最干净的信号,由服务层主动抛出,语义明确,应作为穿透拦截的首选依据
- NullPointerException(空指针):常见于数据库返回 null 后未判空就调用方法。需结合上下文(如请求路径 /user/{id}、参数名 userId)判断是否属于穿透高发接口,再决定是否写空缓存
- MyBatis 的 BindingException 或 TooManyResultsException:说明 SQL 执行异常(如传入非法 ID 导致 WHERE 条件失效),本质是参数污染引发的穿透,也应纳入捕获范围
在 Spring Boot 中集中捕获并响应
使用 @ControllerAdvice + @ExceptionHandler 统一处理,避免散落在各 service 中:
- 对 UserNotFoundException,直接向 Redis 写入短 TTL 空标记:
SET user:123456 "__NULL__" EX 60 - 捕获 NullPointerException 时,先通过 RequestContextHolder 获取当前请求 URI 和参数,仅对已知高风险接口(如 /item/detail)才执行空缓存写入
- 所有异常处理器内禁止远程调用、复杂计算或阻塞 I/O,确保错误响应延迟可控
让缓存注解配合异常流形成闭环
@Cacheable 默认跳过 null 返回值,会加剧穿透。必须做两处改造:
立即学习“Java免费学习笔记(深入)”;
- service 层查询无结果时,不返回
null,而是返回封装好的NullValue对象(如new NullValue("user", userId)) - @Cacheable 配置
unless = "#result instanceof NullValue",确保空值也能进缓存 - 搭配 @CachePut,在 update 接口成功后强制刷新缓存,防止因缓存污染导致误判为“不存在”
把异常频次转化为动态防御能力
高频出现的穿透异常本身是重要运营信号:
- 同一 key 在 1 分钟内触发 5 次 UserNotFoundException,自动将其加入布隆过滤器的“已确认不存在”集合
- 将该事件推送到监控平台(如 Prometheus + Grafana),触发告警并生成穿透热力图
- 可选:对连续失败超阈值的接口路径(如 /api/v1/order/{id}),临时启用参数白名单校验或限流规则


















