异常处理是接口设计的前置环节,需将异常作为契约一部分:明确异常语义、合理选用受检/非受检异常、统一翻译为业务异常并标准化响应、自动化资源管理与边界校验、避免用异常替代正常流程控制。

异常处理不是补救措施,而是接口设计的前置环节。一个健壮的Java应用接口,其容错能力在定义阶段就已埋下伏笔——关键在于让异常成为契约的一部分,而非事后掩盖的漏洞。
明确异常语义,区分可恢复与不可恢复场景
接口应主动声明它可能抛出的异常类型,并赋予每种异常明确的业务含义。比如用户查询接口,NotFoundException表示资源不存在,ValidationException表示参数不合法,ServiceUnavailableException表示依赖服务暂时不可用。这三类异常对应完全不同的客户端应对策略:重试、修正请求、降级或提示用户稍后操作。
- 对客户端能感知并响应的错误(如参数错、资源缺失),使用非受检异常(继承RuntimeException),避免强制调用方写大量空try-catch
- 对底层框架强约束的错误(如数据库连接失败),若上层有明确恢复逻辑(如切换备用数据源),才考虑受检异常;否则统一包装为业务层非受检异常
- 绝不让原始技术异常(如SQLException、HttpClientErrorException)穿透到API层,它们缺乏业务上下文,且暴露实现细节
统一异常翻译与响应建模
真实调用链中,底层异常往往杂乱无章。接口层需做“异常翻译”:捕获原始异常,提取关键信息,封装成结构清晰的业务异常,并附带HTTP状态码、错误码、用户友好提示和可选的调试ID。
- 例如,捕获JDBC的SQLTimeoutException,转换为带有408状态码、ERR_TIMEOUT错误码、提示“请求超时,请稍后重试”的ApiException
- 所有异常最终由全局@ControllerAdvice统一拦截,序列化为标准JSON响应体,确保前端无论遇到哪种错误,都能按同一格式解析和展示
- 日志中记录完整堆栈+业务上下文(如用户ID、订单号、请求路径),便于快速定位问题根因
资源管理与边界防御必须自动化
接口常涉及文件读写、数据库连接、HTTP远程调用等外部资源。这些操作一旦出错,极易引发资源泄漏或状态不一致,进而影响后续请求。
- 所有实现了AutoCloseable的资源(InputStream、Connection、ResponseEntity.getBody()返回的流等),一律用try-with-resources语法,杜绝手动close遗漏
- 对入参做最小必要校验:空值、长度、格式、范围。校验失败立即抛出明确的ValidationException,不进入主业务逻辑——防御越早,问题越可控
- 关键操作添加幂等性标识或事务边界,防止因网络重试导致重复执行;异常发生时,确保事务回滚或补偿动作执行到位
避免异常滥用,保持控制流清晰
异常不是流程控制工具。用异常代替if-else判断正常分支,会严重拖慢性能,且让代码意图模糊。
- 例如,查询用户是否存在,不应靠捕获NotFoundException来决定后续逻辑;而应先调用exists()方法返回布尔值
- 空集合、默认值、可选结果(Optional)等场景,优先用返回值表达,而非抛异常
- 日志级别要匹配异常性质:业务规则违例用WARN,系统级故障用ERROR,调试信息用DEBUG
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











