受检异常推高耦合度,因其将异常传播契约硬编码进方法签名,迫使各层被动修改;应封装为带原始异常上下文的自定义运行时异常,并通过分层策略与全局处理器统一处理。

受检异常(Checked Exception)强制要求调用方处理或声明抛出,本意是提升健壮性,但实际中常导致各层代码被动“传染”,层层添加 throws 或 try-catch,业务逻辑被异常声明稀释,模块间依赖加深——这就是典型的耦合度升高问题。
为什么受检异常会推高耦合度
核心在于它把“异常传播契约”硬编码进方法签名,一旦底层抛出新的受检异常,所有中间调用层都必须修改:要么加 throws,要么加 catch。这违背了开闭原则,也使 DAO 层的变更直接冲击 Service、Controller 层。
- DAO 层抛
SQLException→ Service 层被迫声明throws SQLException或包装再抛 - Service 层若不处理,Controller 层就得接住;若处理,又容易变成日志+忽略,失去意义
- 同一业务流中多个资源操作(DB + 文件 + HTTP),叠加多种受检异常,方法签名迅速膨胀
封装为非受检异常并保留上下文
不是抛弃异常处理,而是把底层受检异常“转译”为语义清晰、无需强制声明的运行时异常,同时不丢失原始堆栈和关键信息。
- 定义统一业务异常基类,如
BusinessException extends RuntimeException - 在 DAO 或工具层捕获
IOException/SQLException,构造新异常并传入原异常作为 cause:throw new BusinessException("数据库查询失败", e) - 确保异常消息包含可定位线索(如 SQL、文件路径、HTTP 状态码),而非泛泛的“操作失败”
分层策略:让异常只在该出现的地方出现
异常不该在每一层都“露面”,而应按职责归位:
- DAO 层:捕获受检异常,封装为领域明确的运行时异常(如
DataAccessException) - Service 层:专注业务规则校验,抛出自定义业务异常(如
InsufficientBalanceException),不处理底层技术异常 - Controller 层:只负责接收并统一响应,不写
try-catch,由全局异常处理器接管
Spring 全局异常处理器兜底
用 @ControllerAdvice + @ExceptionHandler 实现集中响应,彻底剥离业务代码中的异常分支:
- 对
BusinessException返回友好 JSON(含 code、message、timestamp) - 对
NullPointerException等编程错误,记录详细日志并返回 500,不暴露堆栈给前端 - 对未预期的受检异常残留(如漏封装),统一降级为系统错误,避免崩溃











