受检异常治理核心是控制传播、明确意图、统一抽象。通过分层拦截转化、声明式策略收敛、全局处理器闭环及文档契约强化,实现可维护的异常管理体系。

受检异常(Checked Exception)在大型项目中容易导致代码臃肿、调用链污染和维护困难。治理核心不是消灭它,而是控制其传播范围、明确处理意图、统一抽象层级。
分层拦截与异常转化
在各层边界(如 Controller、Service、DAO)主动拦截受检异常,按语义转化为本层统一的业务异常或运行时异常。例如 DAO 层捕获 SQLException,封装为 DataAccessException;Service 层不抛出 IOException,而是转为 FileOperationException 并附带上下文信息。
- Controller 层只暴露有限的、前端可理解的异常(如 BadRequestException、BusinessException),并统一返回结构化错误码
- Service 层内部避免 throws 声明多个受检异常,优先用 try-catch + 封装,保持接口简洁
- DAO 或工具类可保留受检异常,但需配套提供静态工具方法(如 IOUtils.silentClose())降低调用方负担
声明式异常策略收敛
通过模块/包级约定,限制受检异常的使用场景。例如:仅 I/O、JDBC、XML 解析等底层 SDK 调用允许抛出原始受检异常;业务模块禁止在 public 方法签名中声明受检异常。
- 在 API 接口定义(如 OpenAPI/Swagger)中用 error code 替代异常类型,屏蔽技术细节
- 使用 Lombok 的 @SneakyThrows 简化测试或脚手架代码,但生产代码仍需显式处理
- 静态检查(如 Checkstyle、ArchUnit)配置规则,禁止特定包下出现 throws IOException/SQLException
统一异常处理器与日志闭环
建立全局异常处理器(如 Spring 的 @ControllerAdvice),对不同异常类型做差异化响应,并确保所有异常都进入可观测体系。
- 受检异常转化后的业务异常,需携带 traceId、操作人、关键参数,便于问题定位
- 记录 ERROR 日志时附加原始异常堆栈(非简单 toString),但对外响应体中隐藏敏感信息
- 对可重试场景(如网络超时),包装为 RetryableException,由调用方决定是否重试,而非强制上层处理
文档与协作契约强化
把异常处理逻辑写进接口契约文档(如 Javadoc 或内部 Wiki),明确每个方法可能触发的异常类型、触发条件及建议应对方式。
- 公共 SDK 提供的 API 必须标注 @throws,并说明恢复建议(如“请检查文件路径权限”)
- 团队内约定:受检异常代表“调用方必须决策”,运行时异常代表“程序缺陷或不可恢复错误”
- Code Review 重点检查异常是否被忽略(空 catch)、是否误用 RuntimeException 包裹本应通知调用方的失败
不复杂但容易忽略:真正影响治理效果的,往往不是技术方案,而是团队对“异常即契约”的共识和持续落地的纪律性。











