核心是切断受检异常在层间传递链条:DAO层统一转为带业务语义的运行时异常(如DbException),传输层仅处理继承自BaseException的统一错误契约,通过@ControllerAdvice返回标准JSON响应,第三方SDK异常由适配器封装隔离,接口错误行为由OpenAPI文档约定而非throws声明。

避免受检异常在持久层与传输层之间过度耦合,核心是切断“异常类型”在层间直接传递的链条——不让 DAO 层抛出的 SQLException 或 IOException 一路穿透到 Controller,更不把它们写进接口签名里。
持久层只抛运行时异常,不暴露受检异常细节
DAO 接口方法(如 UserDao.findUserById(String id))不应声明 throws SQLException。实现类内部可捕获原始受检异常,统一转为业务语义明确的运行时异常:
- 用自定义运行时异常封装,例如
DataAccessException(Spring 已提供)或项目级DbException - 保留原始异常作为 cause,确保堆栈可追溯:
throw new DbException("查询用户失败", e); - 避免裸 throw new RuntimeException(e),要带业务上下文,比如操作对象、SQL 片段、ID 值等
传输层不感知持久层异常类型,只处理统一错误契约
Controller 或 API 层不 catch SQLException,也不依赖任何 DAO 特有异常。它只面向一套预定义的错误响应模型:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 所有业务异常都继承自同一个基类(如
BaseException),含code、message、timestamp - 全局异常处理器(
@ControllerAdvice)统一拦截BaseException及其子类,返回标准 JSON 格式(如{"code":5001,"msg":"数据库繁忙"}) - HTTP 状态码由异常类型或 code 映射决定,不由原始受检异常驱动
用适配器隔离第三方 SDK 的受检异常
若使用老版本 JDBC 驱动、JPA Provider 或外部 SDK(强制 throws 受检异常),不要让它们污染分层边界:
立即学习“Java免费学习笔记(深入)”;
- 编写 DAO 实现时,将第三方调用包裹在适配器方法内,例如
jdbcTemplate.query(...)替代原生Connection.createStatement() - 适配器内部做一次转换:捕获
SQLException→ 构造DbException→ 抛出 - 对外接口和上层调用者完全看不到
java.sql包下的任何异常类型
接口契约靠文档约定,而非 throws 声明
REST 接口的错误行为通过 OpenAPI 规范描述,而不是 Java 方法签名:
- Swagger 中定义 400 对应参数校验失败(
ParamInvalidException),500 对应系统异常(InternalException) - SDK 封装远程调用时,把 HTTP 异常、IO 异常、序列化异常全部转为统一的
ApiException,调用方只需处理这一种 - 版本迭代中新增错误码,不改动接口方法签名,也不新增 throws 子句

















