Java异常处理需构建有层次、可维护、可观测的响应逻辑,核心在于区分业务异常(自定义checked)、系统异常(不捕获)、外部异常(统一包装),在决策点捕获、携带上下文、设计可观测生命周期。

Java异常处理不是简单地用try-catch包住代码,而是围绕“谁该捕获、何时恢复、如何传递、怎样记录”构建一套有层次、可维护、可观测的响应逻辑。
区分异常类型:按语义而非技术形态归类
Java中Exception分为检查型(checked)和非检查型(unchecked),但真正影响设计的是业务语义。比如网络超时、数据库连接失败、用户输入非法、权限不足,它们的应对策略完全不同。
- 业务异常(如余额不足、订单已取消)应定义为自定义checked异常,强制调用方显式处理,体现契约约束
- 系统异常(如NullPointerException、OutOfMemoryError)属于编程缺陷或不可控故障,不应捕获,而应快速失败并记录堆栈
- 外部依赖异常(如HTTP 503、JDBC SQLException)需包装为统一的服务异常接口,屏蔽底层技术细节,暴露可操作的错误码和重试建议
避免在错误层级捕获:让异常向上冒泡到决策点
DAO层不应吞掉SQL异常并返回null;Service层也不该把所有异常都转成“系统繁忙”。捕获动作应发生在具备上下文信息和处置能力的位置——通常是API入口、事务边界或用户交互层。
- 数据访问层只做轻量转换:将SQLException映射为DataAccessException,不尝试重试或降级
- 业务服务层聚焦流程控制:捕获特定业务异常决定是否重试、补偿或切换策略,不处理底层IO异常
- Web控制器层统一响应:根据异常类型生成对应HTTP状态码、错误码和用户友好的提示消息,同时触发告警或审计日志
用异常携带上下文,而非仅靠日志补充
抛出异常时附带关键业务字段(如订单ID、用户ID、请求参数摘要),比事后从日志中关联追踪高效得多。自定义异常构造器应支持传入context map或builder模式组装元数据。
立即学习“Java免费学习笔记(深入)”;
- 避免String拼接异常消息:“Order 12345 failed” → 改为new OrderProcessingException(orderId, “payment timeout”, context)
- 异常对象中保留原始cause,确保堆栈完整,不因包装丢失根因
- 敏感信息(密码、token)必须过滤,可通过白名单字段或脱敏工具在序列化前处理
设计可观察的异常生命周期
异常不是终点,而是可观测系统的信号源。一次异常发生,应自动触发多维度反馈:记录结构化日志、上报监控指标、触发告警规则、存档用于归因分析。
- 为每类业务异常配置SLA影响标识(如“影响下单成功率”),便于统计错误率趋势
- 在全局异常处理器中集成MDC(Mapped Diagnostic Context),将traceId、userId等注入日志上下文
- 对高频异常(如每分钟超10次)启用熔断或限流,防止雪崩,同时通知负责人介入


















