合理的Java异常体系需从语义、分层、统一响应三层面落地:用业务语义定义异常类型(如InsufficientStockException),统一继承BusinessException并强制错误码与提示;DAO层包装底层异常,Service层抛出业务异常,Controller层仅统一响应;通过MDC增强日志上下文,兜底处理区分环境,拒绝用异常控制流程。

设计合理的Java异常体系,关键不在“怎么捕获”,而在于“异常代表什么”和“谁该对它负责”。一个可维护、易排查、不污染业务逻辑的异常体系,要从语义、分层、统一响应三个层面落地。
用业务语义定义异常类型
避免 throw new RuntimeException("库存不足") 这类模糊表达。每个可预期的业务失败场景,都应有专属异常类:
- 比如 InsufficientStockException、InvalidOrderStatusException、PaymentTimeoutException
- 所有自定义异常统一继承同一基类(如 BusinessException),便于全局拦截
- 构造时强制传入错误码(如 "ORDER_002")和面向用户的提示消息,不依赖堆栈推断语义
- 错误码按模块+序号分层设计(如用户模块以 "USER_XXX" 开头),方便前端识别与运营归因
分层职责必须清晰
各层只处理自己该管的事,不越界、不兜底:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- DAO 层:捕获原生数据库异常(如 SQLTimeoutException),包装成带业务语义的 BusinessException,并保留原始 cause
- Service 层:专注编排逻辑,只在必要时主动抛出业务异常(如校验失败、状态冲突),不处理底层异常
- Controller 层:零业务判断,仅通过 @RestControllerAdvice 统一翻译异常为 HTTP 响应;不重试、不补偿、不记录业务日志
全局兜底 + 上下文增强
异常一旦抛出,就要确保它“说得清、查得到、能复现”:
立即学习“Java免费学习笔记(深入)”;
- 使用 MDC(Mapped Diagnostic Context)在请求入口注入 traceId、userId 等字段,让每条异常日志自带上下文
- @ExceptionHandler 中记录 warn 级别日志,包含错误码、用户提示、关键参数(如订单号、SKU),但不打完整堆栈(留给兜底 handler)
- 对 Exception 或 Throwable 的兜底处理,必须打印完整堆栈并返回 500,但加开关控制——仅在线上开启,开发环境直接暴露堆栈
拒绝用异常控制流程
不是所有异常都需要中断流程。有些是正常业务分支,应设计为可预期、可恢复的信号:
- 例如支付超时,可抛出 PaymentTimeoutException,上游据此触发降级或异步重试,而非当作系统故障报警
- 避免把 NullPointerException、IllegalArgumentException 这类代码缺陷型异常当作业务逻辑分支来 catch 和吞掉
- 受检异常(如 IOException)在现代微服务架构中建议谨慎使用;多数自定义业务异常继承 RuntimeException 更利于分层演进

















