手动校验 if (userId == null) 里只写了 log.warn,没有 return,后面 userMapper.selectById(null) 抛了 NullPointerException,接口 500,日志里 warn 和 error 挨在一起。日志里能对上时间点,这种反而好排。

校验分支和执行逻辑写在同一个方法里,判断完忘了中断。空值继续往下传,直到某一层不接受 null 才炸。异常发生在更深处,报错位置和真正原因隔了好几层。这一点先确认清楚,后面就不用反复试。

grep -n -A3 'userId == null' src/main/java/**/OrderService.java
curl -i 'http://127.0.0.1:8080/api/orders?userId='

「校验后未中断」确认了原因之后,改动尽量只碰那一处,顺手改的东西后面最容易出问题。先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。

别只看报错的最后一行,往上看几行,真正的起因通常在上面。同类问题如果隔一段时间又出现,多半是某个定时任务或重启流程把改动冲掉了。

处理上按这个顺序:校验分支改成抛业务异常 由全局处理统一转 400;把校验抽到方法入口 用 guard clause 提前 return;关键入参在 controller 层再断言一次非空;用 userId 缺失的请求确认返回 400 且没有 NPE。

结果对不对很好判断,缺 userId 的请求返回 400 日志里只有一条 warn 没有 NullPointerException。

作者 9823189