全局异常处理开销极小,真正拖慢系统的是错误日志、堆栈打印、未控传播及低效响应构造;应避免异常处理器中执行I/O、重操作,精简日志与响应体,复用模板,禁用冗余异常创建,并加强异常监控。

全局异常处理本身开销极小,几乎不影响性能;真正拖慢系统的,是异常发生后的错误日志、堆栈打印、未受控的异常传播,以及低效的响应构造逻辑。
避免在异常处理器里做重操作
很多项目会在 @ExceptionHandler 方法中执行耗时动作,比如调用远程服务记录错误、生成完整堆栈快照、写入数据库审计日志等。这些操作会把原本毫秒级的异常响应拉长到数百毫秒,尤其在高并发报错时极易雪崩。
- 日志只打关键字段:traceId、path、status、code、简要消息,禁用
e.printStackTrace()或全量log.error("msg", e) - 异步落库/上报:错误归档类操作应投递到消息队列或使用
@Async线程池处理 - 禁止在异常处理器中调用 FeignClient、JDBC、RedisTemplate 等 I/O 操作
减少异常创建与传播成本
Java 中抛出异常本身比普通方法调用贵一个数量级(涉及栈帧遍历、填充堆栈)。频繁抛出如 IllegalArgumentException 或自定义业务异常,尤其在循环或高频路径中,会显著抬高 CPU 和 GC 压力。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 参数校验优先用
@Valid+BindingResult拦截,避免手动 throw - 对可预期的“非异常”场景(如查无数据),返回空对象或
Optional.empty(),而非抛RuntimeException - 自定义异常类避免重载过多构造器,禁用序列化(
implements Serializable)除非必要
响应体构造要轻量且复用
每次异常都 new 一个 ErrorDetail 对象、拼接 JSON 字符串、再封装成 ResponseEntity,看似简单,但在 QPS 过万时会带来明显对象分配压力和 GC 频率上升。
- 预构建通用错误响应模板(如 400/401/503 的固定 body),用
ResponseEntity.status(code).body(template)直接复用 - 避免在异常处理器中做字符串格式化(如
"用户 " + userId + " 不存在"),改用占位符 + 轻量替换工具(如 Apache Commons Lang 的StringUtils.replaceEach()) - 若用 Jackson 序列化,确保
ErrorDetail类字段精简、无嵌套复杂对象、无 getter 逻辑
监控异常频率,提前干预
性能问题往往始于异常量突增——某个 SQL 超时引发连锁 TimeoutException,或缓存击穿导致大量 NullPointerException。不监控就等于闭眼开车。
- 用 Micrometer 记录每类异常的
counter,接入 Prometheus + Grafana 设置告警阈值 - 在网关层或 Sleuth trace 中标记异常请求,便于链路追踪定位根因
- 定期分析日志中的异常 TOP N,区分是偶发故障还是设计缺陷(如本该用 if 判断却 throw 了异常)


















