一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

受检异常处理的重构策略:异常包装器模式

时间:2026-06-22 09:37:58 编辑:袖梨 来源:一聚教程网

将受检异常包装为有业务语义的运行时异常,旨在表达业务意图、厘清DAO/Service职责边界、支撑系统演进;DAO层统一转化SQLException等为DataAccessException或自定义PersistenceException,Service层专注业务逻辑与基于异常类型的重试/降级决策,禁止裸throw RuntimeException,须按场景归类封装(如InvalidRequestException、ExternalServiceException),提供cause构造器并记录完整堆栈,辅以全局异常处理器与可观测性建设。

把受检异常包装成运行时异常,不是为了躲编译检查,而是让异常真正表达业务意图、理清各层职责、支撑系统持续演进。

在 DAO 与 Service 边界做一次干净剥离

旧代码常让 SQLException、IOException 直接穿透到 Controller,方法签名堆满 throws,业务逻辑被技术细节干扰。重构关键一步是划定“异常转化区”:

  • DAO 层(或 MyBatis Mapper 实现类)统一捕获底层 IO/SQL 异常,转为语义明确的运行时异常,比如 Spring 的 DataAccessException,或自定义 PersistenceException
  • Service 层方法不再声明 throws SQLException 等,只关注业务规则;重试、降级等决策也基于运行时异常类型,而非原始异常类名
  • 禁止在 Service 内部 catch 再 throw 原始受检异常——这等于把技术细节重新塞回业务逻辑

用带业务含义的运行时异常替代泛型 RuntimeException

throw new RuntimeException(e) 虽能过编译,但会丢失所有可维护性。应按失败场景归类封装:

  • 参数解析失败(如 JSON 反序列化错误、日期格式不匹配)→ IllegalArgumentException 子类,例如 InvalidRequestException
  • 外部服务不可用或超时 → ExternalServiceException,携带服务名、超时阈值等上下文字段
  • 状态不一致(如订单已支付却尝试取消)→ IllegalStateException 子类,例如 IllegalOrderStateException
  • 所有自定义异常必须提供 Throwable cause 构造器,并在日志中记录原始异常类名和消息

为 Stream、CompletableFuture 等现代 API 提供安全适配

受检异常会直接卡住 lambda 表达式。不要每个地方都写 try-catch,而是轻量封装:

  • 对 Files.readAllLines()、BufferedReader.lines() 等标准 IO 方法,优先使用 Java 8+ 内置的 UncheckedIOException 包装
  • 对自定义 IO 逻辑,提供工具方法如 IOFunctions.unchecked(Files::readAllLines),内部完成 try-catch + UncheckedIOException 构造
  • 工具方法返回 Function<Path, List<String>> 这类无异常声明的接口,保持流式操作简洁

配套全局处理器与可观测性补全链路

包装为运行时异常后,问题定位能力不能下降。必须同步落地支撑机制:

  • 统一用 @ControllerAdvice 或 @ExceptionHandler 拦截 BusinessException 及其子类,返回结构化错误响应
  • 所有异常在抛出前打点埋标,记录异常分类、触发路径、重试次数等维度,接入监控告警
  • 日志中保留完整堆栈,确保原始异常 cause 和当前层包装异常同时可见,支持逐层下钻排查

热门栏目