推荐将Java原生异常映射为统一业务码。需分系统级、校验级、流程级三层设计,通过枚举或注解实现可配置映射,响应体包含code、message、traceId及可选detail,并注意拦截顺序、第三方异常显式转换与事务异常不被吞没。

可以,而且推荐这么做。Java原生异常对前端或调用方不友好,直接暴露堆栈或异常类名会泄露实现细节,也不便于前端统一处理。将异常映射为可读、可分类、可追踪的统一业务码,是微服务和API中台场景下的常见实践。
业务码需分层设计
不能所有异常都塞进一个码表。建议按层级划分:
- 系统级码:如 500101(数据库连接失败)、500202(Redis超时),对应 RuntimeException 或底层基础设施异常,通常需告警+人工介入
- 校验级码:如 400101(手机号格式错误)、400203(余额不足),来自参数校验或业务规则拦截,前端可直接提示用户
- 流程级码:如 409101(订单已支付,不可重复提交),属于特定状态冲突,需结合上下文判断,前端可引导重试或跳转
异常到码的映射要可配置、可扩展
- 用枚举定义业务码,每个枚举项关联一个异常类(如 red">BizCodeEnum.ILLEGAL_PARAM → IllegalArgumentException),启动时注册到全局映射器
- 基于注解驱动:自定义 @BizCode(code = "400101", msg = "用户名已存在") 标在自定义异常类上,统一异常处理器自动提取
封装响应体时保留必要上下文
返回给前端的不只是 code 和 message。建议结构包含:
立即学习“Java免费学习笔记(深入)”;
- code:统一业务码(字符串或整型,推荐字符串,便于扩展前缀如 "USER_400101")
- message:面向用户的简明提示(非开发日志)
- traceId:链路唯一标识,用于日志溯源
- detail(可选):仅限内部调试,如原始异常类名、关键参数快照,生产环境默认关闭
避免踩坑的几个关键点
实际落地时容易忽略:
- Spring 的 @ControllerAdvice 拦截顺序:确保自定义异常优先于 ResponseEntityExceptionHandler,否则 400/500 被提前吃掉
- 第三方 SDK 抛出的异常(如 HttpClientException、FeignException)需显式捕获并转码,不能依赖通用 Exception 捕获——否则可能把网络超时当成“参数错误”
- 事务方法内抛出的异常若被吞掉(比如 try-catch 后没 rethrow),业务码不会生效,且事务可能未回滚
基本上就这些。核心不是“能不能转”,而是“怎么转得清晰、可维护、不漏不重”。统一业务码不是加一层包装,而是建立前后端对齐的问题语义体系。










