java中包装异常必须用带throwable cause参数的构造器,如throw new serviceexception("订单创建失败", e),确保原始异常完整透传、不丢失堆栈和类型;自定义异常需显式实现该构造器并调用super(message, cause)。

关键不是堆栈嵌套得多,而是每一层都明确“谁负责什么”,原始异常必须从最底层完整透传到最外层,中间不丢失、不覆盖、不静默吞掉。
用带 cause 的构造器包装,别拼消息
捕获异常后要向上抛,必须用支持 Throwable cause 参数的构造器:
- ✅ 正确:
throw new ServiceException("订单创建失败", e); - ❌ 错误:
throw new ServiceException("订单创建失败: " + e.getMessage());(丢堆栈、丢类型、丢 cause) - ⚠️ 避免:
new RuntimeException("xxx").initCause(e)(构造后设 cause 易出错,且只能调一次)
自定义异常必须显式支持 cause
自己写的异常类,如果没提供带 cause 的构造器,编译会报错或退化为无链版本:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 必须写:
public BusinessException(String msg, Throwable cause) { super(msg, cause); } - 否则即使你写了
new BusinessException("msg", e),也会因找不到匹配构造器而失败
切面和拦截器里最容易断链,要重点检查
Spring AOP、Feign 拦截器、网关过滤器等多层代理场景,是原始异常丢失的高发区:
- 不要在
@Around的 catch 块中写throw ex;(会重置堆栈),应直接throw; - 所有包装操作必须传
e作 cause,比如throw new ApiRuntimeException("调用超时", e); - 避免空
catch (Exception e) {}或只记录日志却不 re-throw - 用
@Order控制切面顺序,确保日志/监控切面在最外层,能拿到完整 cause 链
调试和日志要看对地方
保留了链,但看错了方式,等于白留:
- 打印堆栈必须用
e.printStackTrace()或日志框架的logger.error("xxx", e),它会自动展开 Caused by 层级 - 别只打
e.getMessage()或e.toString()——它们不包含 cause 和深层栈帧 - 生产环境建议提取 root cause:用
ExceptionUtils.getRootCause(e)(Apache Commons)或手动递归遍历(加空值、深度、循环引用防护)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










