非受检异常与业务异常应分层协作:dao/service层只抛非受检异常;controller层通过@exceptionhandler统一响应,业务异常返回200+错误码,系统异常返回5xx;网关层包装远程异常并补全链路信息;全局处理器按语义分流,配套错误码中心化、日志标准化与分级告警。

非受检异常(Unchecked Exception)和业务异常的统一架构设计,核心不是“捕获所有异常”,而是让它们在系统中各司其职、语义清晰、响应一致。分布式环境下,滥用 try-catch 或混用异常类型,反而会掩盖故障本质、阻碍链路追踪、干扰熔断决策。
明确分层职责:谁该抛、谁该接、谁该转
统一架构的前提是分层契约清晰:
- DAO/Service 层:只抛出非受检异常(继承 RuntimeException),包括自定义业务异常(如 OrderNotPayedException)和系统异常(如 DbConnectionTimeoutException)。不 throw 受检异常,避免跨服务序列化失败与接口污染。
- Controller 层:不写 try-catch,只声明 @RestControllerAdvice 全局拦截;对不同异常类型做语义区分——业务异常返回 200 + 错误码,系统异常返回 5xx 状态码。
- 网关/Feign 层:将远程调用异常(如 TimeoutException、HystrixRuntimeException)统一包装为标准非受检异常,并补全 traceId 和上游服务名,确保链路可溯。
业务异常必须是非受检的,且带结构化元信息
业务异常不是“出错了”,而是“业务规则被触发了”。它不该打断调用链,也不该触发熔断,但必须携带可解析的上下文:
- 继承 RuntimeException,避免强制 throws 污染接口签名;
- 构造时必传 errorCode(如 “ORDER_PAYMENT_EXPIRED”)和用户友好 message(如 “订单支付已超时,请重新下单”);
- 支持嵌套 cause,便于排查是否由底层系统异常引发(例如库存校验失败 → DB 查询超时);
- 日志记录级别设为 WARN,不打印完整堆栈,仅记录 errorCode、requestId、关键参数(如 orderId、userId)。
全局异常处理器要按语义分流,不能一锅煮
@ControllerAdvice 不是兜底垃圾桶,而是语义路由中枢:
- 捕获自定义业务异常(如 extends BaseBusinessException)→ 返回统一 JSON 结构:{ "code": "ORDER_NOT_FOUND", "msg": "订单不存在", "data": null },HTTP 状态码 200;
- 捕获系统级非受检异常(如 SQLException、RedisConnectionFailureException)→ 记录 ERROR 日志 + 上报监控,返回 { "code": "SYSTEM_ERROR", "msg": "服务暂时不可用" },HTTP 状态码 500;
- 对 Error(如 OutOfMemoryError)直接拒绝处理,仅记录 fatal 日志并触发 JVM 优雅退出钩子;
- 所有响应体字段需与前端约定一致,禁止在 message 中拼接堆栈或技术细节。
配套机制:让异常真正“可治理”
光靠代码规范不够,需工程化支撑:
- 错误码中心化管理:所有 errorCode 放入配置中心或枚举类,禁止硬编码字符串;
- 异常日志模板标准化:每条异常日志固定包含 traceId、spanId、service、errorCode、params(脱敏后);
- 告警分级:业务异常不告警,系统异常按错误码分级(如 DB 相关 → P0,缓存超时 → P2);
- 灰度开关:对高频业务异常(如“优惠券已领完”)支持动态降级为 INFO 日志,减少日志洪峰。











