java异常链需强制通过构造器嵌入cause以保留根源,日志须完整输出堆栈,响应体脱敏且统一code/message,禁用initcause()确保线程安全与审计合规。

Java利用异常链保留历史痕迹,核心是让每一次异常都携带可追溯的原始上下文;配合安全合规审计,则要求异常处理过程不泄露敏感信息、不绕过日志留痕、不破坏调用链完整性。这不是技术炫技,而是保障故障可定位、行为可审计、责任可认定的关键实践。
强制 cause 嵌入,确保根源不丢失
所有业务异常必须继承 RuntimeException,并提供 public XxxException(String message, Throwable cause) 构造器,且内部必须调用 super(message, cause)。这样能保证底层 SQLException、FeignException、RedisConnectionException 等技术异常不被吞掉。
- 禁止写
throw new BusinessException("库存不足")—— 缺失 cause 会切断链路 - 正确写法:
throw new BusinessException(ORDER_STOCK_SHORTAGE, "库存不足", e),其中 e 是捕获的原始异常 - 标准异常(如 IOException)已内置 cause 支持,直接复用,无需重写构造逻辑
日志记录必须带堆栈,禁用 getMessage() 单独打点
安全审计要求异常行为全程留痕,而仅记录 e.getMessage() 会导致根因丢失、无法复现、违反等保日志完整性要求。
- 正确日志写法:
log.error("订单创建失败", e)—— SLF4J/Logback 会自动输出完整堆栈及 Caused by 链 - 禁止:
log.error("订单创建失败: " + e.getMessage())—— 敏感参数可能拼入日志,且无堆栈 - 敏感字段(如用户ID、订单号)若需在日志中标识,应通过 MDC 注入 traceId + bizId,而非拼进 message
响应体脱敏输出,前端按 code 自治,后端不透传技术细节
面向前端的错误响应是审计重点区域:不能暴露数据库表名、类名、路径、IP 等基础设施信息,也不能返回原始异常 message。
- 统一由 @RestControllerAdvice 拦截,响应结构固定为:
{"code":"ORDER_003","message":"订单ABC123已超时","traceId":"abc-xyz-789"} - message 必须来自 ErrorCode 枚举模板填充,禁止直接用原始异常 message
- traceId 全链路透传,前端自动上报至监控平台,与操作日志、审计日志关联分析
- 兜底异常(如未捕获 Exception)响应 code 固定为 INTERNAL_ERROR,message 固定为“服务暂不可用”,绝不返回堆栈片段
禁用 initCause(),杜绝运行时状态污染风险
initCause() 虽支持补设 cause,但其“只能调用一次”和“多线程下状态不安全”的特性,不符合安全审计对确定性、可重现性的要求。
- 它仅适用于老异常类无 cause 构造器的临时兼容场景,新代码严禁使用
- 现代异常链应全部通过构造函数完成初始化,JVM 保证线程安全与堆栈一致性
- 审计检查项之一:扫描代码中是否出现
.initCause(调用,发现即视为不合规
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











