异常驱动模式要求以异常表达业务意图,而非搬运技术故障;需在DAO层转化受检异常为语义明确的运行时异常,Service层专注业务规则,自定义异常须带业务含义与原始异常上下文,并配套全局处理与可观测性机制。

从受检异常转向异常驱动模式,不是简单地把 throws IOException 改成 throw new RuntimeException(e),而是以异常为线索重新组织代码职责、明确错误语义、收紧调用契约。核心是让异常成为业务意图的表达载体,而非技术故障的搬运工。
在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这类无异常声明的接口,让流式操作保持简洁
配套全局处理器与可观测性补全链路
包装为运行时异常后,问题定位能力不能下降。必须同步落地支撑机制:
- Spring Boot 中配置
@ControllerAdvice全局异常处理器,统一响应格式(如 status、code、message、traceId) - 关键异常类型打标(如
@Loggable注解),触发告警或自动创建工单 - 日志中保留原始异常堆栈,并注入业务上下文(如运单号、用户ID、请求路径)
- 配合 APM 工具(如 SkyWalking)追踪异常发生位置与调用链路,实现“异常即指标”


















