java封装异常需保留原始堆栈与cause链:日志记录须用logger.error(msg, throwable);自定义异常构造时传入原始异常作cause;禁用空throw;日志配置需含%ex以输出完整堆栈。

Java 封装时提供清晰的错误堆栈信息,核心在于不破坏原始异常链、不丢失上下文、不掩盖调用位置。封装本身不是掩盖错误,而是让错误更易理解、更易定位、更贴合业务语义。
用 logger.error(msg, throwable) 记录原始异常
封装方法内部捕获异常后,若需记录日志,必须把 Throwable 对象作为第二个参数传给 error 方法:
- ✅ 正确:logger.error("转账失败,账户ID={}", accountId, e); —— 完整输出消息 + 堆栈 + cause 链
- ❌ 错误:logger.error("转账失败,账户ID=" + accountId + ", " + e); —— 只输出 e.toString(),堆栈全丢
- ❌ 错误:logger.error("转账失败:{}", e.getMessage()); —— e 被忽略,无堆栈
封装异常时保留 cause 链
若要抛出自定义异常(如 BusinessException),必须将原始异常设为 cause:
- ✅ 正确:throw new BusinessException("余额不足", e); —— 构造器中调用 super(message, cause)
- ✅ 正确:throw new BusinessException("余额不足", e).setErrorCode("BALANCE_001"); —— 在自定义字段外仍保留原始堆栈
- ❌ 错误:throw new BusinessException("余额不足"); —— 原始 IOException 或 SQLException 信息彻底丢失
避免在封装层吞掉或重抛无堆栈异常
常见反模式是“捕获 → 记录 → 空 throw”,这会切断堆栈源头:
- ❌ 危险写法:
catch (SQLException e) {<br> logger.warn("DB 查询异常", e);<br> throw new RuntimeException("查询失败"); // 堆栈从这行开始,原始位置没了 - ✅ 安全写法:
catch (SQLException e) {<br> logger.error("DB 查询异常(用户ID={})", userId, e);<br> throw new DataAccessException("查询用户信息失败", e); // 继承 RuntimeException 并传入 cause
日志配置确保 %ex 渲染堆栈
即使代码写对了,Logback/Log4j2 配置不当也会导致堆栈不显示:
- 检查 appender 的 pattern 是否含 %ex 或 %xEx(支持嵌套异常)
- 避免只用 %m 占位符,它只输出消息字符串
- 推荐模板节选:%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n%ex
- 确认 root logger 级别不低于 ERROR,且未被过滤器静默拦截
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











