Java异常处理:系统底层的异常信息捕获与封装技巧

作者:袖梨 2026-07-08
应使用自定义业务异常类(如BusinessException)封装底层异常,保留cause链并面向业务构造消息,区分业务异常与系统异常处理策略,日志记录需完整展开异常链以利排查。

捕获并封装底层异常,核心是让业务层不被技术细节干扰,同时保留足够线索用于排查。关键不在“包得有多深”,而在“包得是否准确、安全、可维护”。

用自定义异常类统一语义

不要直接把 SQLException、IOException 等抛到 service 或 controller 层。应定义轻量级的业务异常基类(如 BusinessException),继承 RuntimeException,并提供带 cause 的构造函数:

  • 必须有 public BusinessException(String msg, Throwable cause),否则原始堆栈在构造时就被截断
  • 避免只写 new BusinessException("操作失败") —— 这会丢失所有根因信息
  • 子类按语义区分即可,比如 UserNotFoundException、InsufficientBalanceException,无需为每个错误码新建异常类

包装时保留 cause,但控制信息粒度

底层异常要传进去,但消息内容要面向业务,而非面向数据库或网络:

  • ✅ 正确:throw new BusinessException("订单创建失败,请稍后再试", e)
  • ❌ 错误:throw new BusinessException("MySQL connection refused: localhost:3306", e) —— 暴露环境细节,且前端不该看到
  • 日志记录用 log.error("create order failed", e),SLF4J 会自动展开整个 cause 链

分清两类异常,处理策略不同

不是所有异常都该被“友好包装”:

立即学习“Java免费学习笔记(深入)”;

  • 业务异常(如参数校验失败、库存不足):预期内发生,前端可直接展示 getMessage(),通常不记 ERROR 日志
  • 系统异常(如 DB 连接中断、Redis 超时):属于基础设施故障,应记录 ERROR 日志 + 上报监控,前端统一返回“系统繁忙”,不暴露细节
  • IO 类异常(如文件读取失败)若属正常流程分支(例如用户上传空文件),可直接处理,不必再包一层 BusinessException

调试时快速定位根本原因

别依赖 e.toString() 查根因,它只显示当前异常的类名和消息:

  • 要用 e.getCause() 逐层获取,直到返回 null;Java 7+ 可配合 ExceptionUtils.getRootCause(e)(Apache Commons)
  • 避免手动拼接日志时只写 e.getMessage() —— 可能为空、被重写或截断
  • 如果需提取关键字段(如 SQL 状态码、HTTP 状态),应在包装时主动解析并存入异常 context,而不是靠堆栈倒推

相关文章

精彩推荐