自定义业务异常应继承runtimeexception,提供含消息、原因、错误码的构造方法,在service层校验失败时主动throw,并通过@controlleradvice统一响应。

Java 基础中自定义异常类并抛出业务错误,关键在于明确异常类型选择、规范构造方法、在合适位置用 throw 主动触发,并让调用方能清晰感知和处理。不是所有错误都该用异常,但真正属于业务规则失败的场景(比如“用户已注销,不能提交订单”),就该用自定义异常来表达。
选对继承关系:业务异常通常用 RuntimeException
多数业务异常不需要强制调用方 try-catch,所以推荐继承 RuntimeException。这样既保持语义清晰,又避免污染方法签名:
- 继承 Exception → 编译器强制 throws 或 try-catch,适合必须由上层决策恢复的场景(如支付余额不足,需跳转充值页)
- 继承 RuntimeException → 更轻量,适合校验失败、状态非法等无法现场恢复、但需快速中断并返回提示的业务判断(如参数格式错、订单非待支付状态)
- 不要继承 Error,那是系统级崩溃,业务代码不该抛
写好异常类:至少两个构造方法 + 可选错误码
一个实用的业务异常类,至少提供以下构造方法:
-
BusinessException(String message):最常用,用于简单提示,如
throw new BusinessException("手机号不能为空") - BusinessException(String message, Throwable cause):用于包装底层异常(如 DAO 抛出的 SQLException),保留原始堆栈便于排查
- 建议额外加 BusinessException(ErrorCode code, String message):把错误码(如枚举
USER_NOT_FOUND)和消息分离,方便统一管理提示文案和多语言支持
错误码不建议硬编码在异常类里,而是抽成独立枚举或配置项,异常类只持有一个 code 字段和对应 getter。
在核心逻辑中用 throw 主动抛出
异常不是被动发生的,而是你主动“声明失败”。常见位置包括 Service 层的校验点:
- 参数合法性检查后:如
if (StringUtils.isBlank(phone)) { throw new BusinessException("手机号格式无效"); } - 业务状态判断后:如
if (!OrderStatus.WAIT_PAY.equals(order.getStatus())) { throw new BusinessException("订单当前状态不允许支付"); } - 关键资源不存在时:如查不到用户、商品库存为零,直接 throw,而不是返回 null 或 false 让上层猜含义
注意:不要用异常做流程控制(比如用 catch 来实现 if-else),只在真正“出错了”且需要中断当前执行流时才 throw。
配合全局处理器统一响应
单有自定义异常还不够,得让整个系统知道怎么响应它。Spring 项目中常用 @ControllerAdvice + @ExceptionHandler 捕获:
- 捕获
BusinessException,返回标准 JSON 格式:{"code": 1002, "msg": "用户不存在", "data": null} - 可结合 ErrorCode 枚举自动映射 HTTP 状态码(如 400 或 404),但注意:错误码本身是业务概念,不等于 HTTP 状态码
- 日志中记录 error level + 异常全类名 + 错误码,便于监控告警
这样前端拿到一致结构,运维看到可读日志,开发也不用每个接口都写 try-catch。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











