统一异常码必须用枚举,因其具备类型安全、防拼写错误、可集中管理及附带HTTP状态码等优势;业务异常与系统异常须分包隔离,前者封装错误码无堆栈,后者保留原始堆栈;全局处理器应动态设状态码并统一返回Result结构。

统一异常码该不该用枚举
该用,而且必须用。字符串或整数常量定义异常码极易导致拼写错误、重复定义、无法集中管理,IDE 也难做校验。枚举天然具备类型安全、可枚举、可附带描述和 HTTP 状态码等能力。
推荐结构:ErrorCode 枚举实现 ErrorCodeInterface 接口(含 getCode()、getMessage()、getHttpStatus()),避免后续扩展时被迫改大量 switch。
public enum ErrorCode implements ErrorCodeInterface {
USER_NOT_FOUND(40001, "用户不存在", HttpStatus.NOT_FOUND),
PARAM_VALIDATION_ERROR(40002, "参数校验失败", HttpStatus.BAD_REQUEST),
SYSTEM_BUSY(50001, "系统繁忙,请稍后重试", HttpStatus.INTERNAL_SERVER_ERROR);
private final int code;
private final String message;
private final HttpStatus httpStatus;
ErrorCode(int code, String message, HttpStatus httpStatus) {
this.code = code;
this.message = message;
this.httpStatus = httpStatus;
}
@Override
public int getCode() { return code; }
@Override
public String getMessage() { return message; }
@Override
public HttpStatus getHttpStatus() { return httpStatus; }
}
业务异常和系统异常要不要分包处理
要分,且必须物理隔离。混在一起会导致日志归因困难、监控指标失真、前端兜底策略混乱。
-
BizException继承RuntimeException,只封装ErrorCode和可选业务上下文(如订单号),不带堆栈(或显式抑制) -
SystemException继承RuntimeException,用于包装IOException、SQLException等,保留原始异常堆栈,便于排查基础设施问题 - 全局异常处理器中:对
BizException返回errorCode + message;对SystemException记录完整堆栈并返回泛化错误码(如50099),绝不暴露敏感信息
@ResponseStatus 和统一返回体怎么配合
别用 @ResponseStatus 注解在自定义异常类上。它会强制绑定 HTTP 状态码,但实际中同一个错误码可能对应不同状态(比如 USER_NOT_FOUND 在查询接口返回 404,在创建接口前置校验时可能返回 400)。
立即学习“Java免费学习笔记(深入)”;
正确做法是:所有异常都走统一的 @ControllerAdvice 处理器,根据 ErrorCode.getHttpStatus() 动态设状态码,并统一包装成 Result 结构:
@ExceptionHandler(BizException.class) public ResponseEntity> handleBizException(BizException e) { ErrorCode code = e.getErrorCode(); return ResponseEntity.status(code.getHttpStatus()) .body(Result.fail(code.getCode(), code.getMessage())); }
这样既保持协议层语义清晰,又避免状态码被“写死”在异常类型里,方便灰度或兼容性调整。
错误码前缀要不要加业务域标识
要看规模。单体或小团队初期可省略;微服务多、模块超 5 个、跨团队协作时,强烈建议加前缀,比如 USER_40001、ORDER_50002。
原因很实际:
- 避免合并 PR 时无意覆盖他人错误码(整数易冲突,字符串前缀天然隔离)
- 日志/监控平台按前缀聚合告警更精准(比如只看
ORDER_*的 5xx 上升趋势) - 前端可根据前缀做差异化提示(
USER_*跳登录页,PAY_*跳支付重试)
注意:前缀应为英文大写下划线风格,不建议用数字或点号(user.40001 或 1001_40001 都增加解析负担)。
真正麻烦的不是设计规则,而是让所有人——包括新来的同事、临时支援的测试、第三方对接方——能一眼从错误码反查到定义位置、含义、修复责任人。所以枚举类必须配 Javadoc,CI 流程里加校验:新增枚举值必须有非空 @since 和 @see 指向需求单或 Wiki 页面。










