自定义异常应精准表达业务失败语义,继承RuntimeException并命名体现场景(如InsufficientStockException),构造时携带关键上下文,由全局@ExceptionHandler统一处理并响应,使异常成为流程分支信号而非错误。

用自定义异常中断复杂业务流,关键不是“打断”,而是“精准表达失败语义”并让整个流程自然退出——不靠 return、不靠布尔标志、不层层透传错误码,而是用异常本身作为控制信号和上下文载体。
设计有业务含义的异常类
不要继承 Exception 写一堆 checked 异常,除非你真希望调用方必须显式处理(比如文件读取失败需重试或降级)。对业务规则类中断,优先继承 RuntimeException,但必须是**有意义的子类**:
- 命名体现场景:如
InsufficientStockException、OrderAlreadyPaidException、IdentityVerificationFailedException - 构造时携带关键上下文:订单号、用户ID、库存余量、验证失败原因等,不只靠 message 字符串
- 可选增加错误码字段(如
ERR_STOCK_SHORTAGE),便于前端展示或监控归类
在业务逻辑中直接抛出,不包装不掩盖
当校验失败、状态不满足、前置条件缺失时,立刻抛出自定义异常。避免“先 return false,再在外层判断跳转”这类弱语义写法:
- ✅ 正确:
if (order.getStatus() != DRAFT) throw new OrderStatusInvalidException(order.getId(), order.getStatus()); - ❌ 避免:
if (!canSubmit(order)) { return Result.fail("状态不可提交"); }—— 把业务语义降级为字符串
这样做的好处是:异常栈天然记录执行路径;日志里能一眼看到“在哪、因何、对谁”中断;后续统一拦截时能精准识别并响应。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
用全局异常处理器收口响应
Spring 项目中,绝不在每个 Service 方法或 Controller 里写 try-catch。统一用 @RestControllerAdvice 拦截你的业务异常:
- 捕获
BusinessException或其子类,转换为标准 API 响应(如Result.fail(e.getErrorCode(), e.getMessage())) - 对非业务异常(如
NullPointerException)单独捕获,记录 ERROR 日志并返回通用系统错误,避免泄露内部信息 - 可结合 AOP,在抛出前自动注入 traceId、用户ID 等上下文,提升排查效率
配合流程引擎或状态机时更清晰
如果业务本身是多步骤流程(如下单→扣库存→发券→通知),每个步骤抛出不同异常,上层调度器可据此决策:
-
StockLockTimeoutException→ 触发库存释放 + 重试 -
VoucherExpiredException→ 跳过发券,记录补偿任务 -
NotificationFailedException→ 不中断主流程,异步重推
异常不再是“错误”,而是流程的**分支信号**,比 if-else 判断状态码更内聚、更易测试、更易扩展。

















