错误码映射需体现故障语义、支持精准识别与根因追踪:按异常类型分层映射,封装于errorcoderesolver接口,保留原始异常上下文,日志记录完整诊断信息,并支持动态兜底、熔断与监控告警。

在catch块中做错误码映射,核心不是“把异常转成码”,而是让每个错误码真实反映故障语义、可被下游精准识别、且不丢失根因线索。简单地用if-else硬编码映射或统一返回ERROR_UNKNOWN,会削弱可观测性和问题定位效率。
按异常类型分层映射,拒绝一刀切
不同异常代表不同失败维度,错误码应体现这一差异:
-
网络类异常(如
SocketTimeoutException、ConnectException)→ 映射为ERR_NETWORK_TIMEOUT或ERR_SERVICE_UNREACHABLE,便于前端触发重试或降级UI -
数据访问异常(如
SQLException)→ 不直接映射为通用DB错误,而根据getSQLState()或getErrorCode()细分:23505(唯一约束冲突)→ERR_DUPLICATE_KEY;08001(连接拒绝)→ERR_DB_CONNECTION_REFUSED -
业务校验异常(如自定义
InvalidOrderException)→ 直接映射为语义明确的ERR_INVALID_ORDER,避免再绕到IllegalArgumentException层级 -
系统级异常(如
OutOfMemoryError、StackOverflowError)→ 不映射业务码,应记录严重告警并快速终止,防止状态污染
封装映射逻辑,避免catch块内散列判断
把错误码决策从catch块中解耦出来,提升复用性与可测试性:
- 定义一个
ErrorCodeResolver接口,提供resolve(Throwable e)方法 - 实现类按策略分组:如
SqlErrorCodeResolver专处理JDBC异常,HttpClientErrorCodeResolver处理HTTP客户端异常 - catch块中只调用一行:
int code = errorCodeResolver.resolve(e);,不再出现instanceof嵌套或长串if
保留原始异常上下文,支撑根因追踪
错误码只是对外暴露的简明标识,内部日志和监控必须携带足够诊断信息:
- 日志中记录:
errorCode=ERR_NETWORK_TIMEOUT, exceptionType=SocketTimeoutException, cause=java.net.SocketTimeoutException: Read timed out - 若使用自定义异常包装,构造时务必传入原始异常:
throw new BusinessException("下单超时", e); - 对Web场景,响应体中除
code字段外,建议增加traceId和脱敏后的errorKey(如timeout_read_5000ms),方便日志聚合与告警关联
支持动态兜底与熔断反馈
错误码映射本身也需具备容错能力,防止映射逻辑出错导致二次失败:
- resolver实现中设置默认兜底码(如
ERR_UNKNOWN),但同时记录WARN日志:“未匹配异常类型[xxx],启用兜底码” - 对高频触发同一错误码的请求(如1分钟内同
ERR_DB_CONNECTION_REFUSED超10次),可触发轻量级内存熔断,后续请求直接返回该码并跳过实际调用,避免雪崩 - 将错误码分布上报Metrics(如Micrometer的
counter),当某码突增时自动触发告警,推动上游服务治理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











