用 RuntimeException 包装异常是为了将检查型异常转为非检查型,避免破坏封装性并控制信息粒度;必须保留原始异常作为 cause 并提供业务上下文消息,禁用 addSuppressed() 主动包装主异常,自定义异常应继承 RuntimeException、提供 errorCode 字段且不序列化。
为什么要用
包装异常?
直接抛出底层异常(比如
、
)会让上层调用方被迫处理与业务无关的检查型异常,破坏
封装性
。更严重的是,原始异常堆栈可能暴露敏感路径或内部实现细节。用
包装,本质是做一层语义转换:把“系统级失败”转为“业务不可达”,同时控制信息粒度。
常见错误是只写
而不传原始消息,导致日志里只有
,没有上下文;或者直接丢弃
,写成
,彻底丢失根因。
必须保留原始异常作为 cause:
消息中要包含关键业务标识,比如操作对象、ID:
避免在消息里拼接
敏感数据
(如密码、token),哪怕是在开发环境
和
的区别在哪?
是异常链的主干,代表“因为什么而失败”,每个异常最多一个;
是 Java 7 引入的机制,用于记录 try-with-resources 自动关闭时发生的额外异常,可有多个。包装异常时,绝大多数场景只应设置
。
误用
包装主异常会导致诊断混乱:日志工具(如 Logback)默认只打印
链,
往往被忽略;监控系统也极少采集 suppressed 异常。
立即学习
“
Java免费学习笔记(深入)
”;
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
正确做法:用构造函数传入
,例如
仅在资源清理失败需并行上报时才手动调用
自定义异常类应显式提供带
参数的构造函数,并调用
自定义异常要不要继承
?
取决于是否希望调用方强制处理。面向 API 或服务层,推荐继承
(如
、
),让业务代码专注逻辑而非异常模板;但 DAO 层若仍需向上透传 JDBC 异常,可保留检查型异常(如
),再由 service 层统一包装。
容易踩的坑是混用:同一模块里既有
又有
,造成异常处理策略断裂。更隐蔽的问题是自定义异常没重写
或没提供
字段,导致统一错误响应时无法提取结构化信息。
所有自定义运行时异常必须提供
构造函数
建议增加
或
字段,用于
前端
识别错误类型
避免让自定义异常实现
,除非明确需要跨 JVM 传输(如 RMI)
Spring 中
怎么配合包装异常?
包装只是第一步,真正起作用的是统一拦截和转化。Spring 的
能捕获你包装后的异常,但前提是它能匹配到类型——所以自定义异常必须是具体类,不能只靠
泛泛而谈。
典型错误是写成
,结果把
也一并吞掉,掩盖了本该快速暴露的编程错误。另一个问题是没设置
,导致 HTTP
状态码
始终是 500,无法区分客户端错误(4xx)和服务端错误(5xx)。
按具体异常类型注册 handler,优先级高于泛化类型
在 handler 内不要重新包装异常,否则会破坏原有 cause 链
如果需要补充上下文(如 traceId),用 MDC 注入,而不是拼到异常消息里
异常包装不是加个
就完事。最常被忽略的是:包装后是否还保有足够诊断信息、是否与全局错误处理机制对齐、以及自定义异常是否真的能被准确识别和分类。这三个点没对齐,包装就只是把问题藏得更深了一点。
throw new RuntimeException(e)IOExceptionSQLExceptionRuntimeExceptionnew RuntimeException(e)java.lang.RuntimeExceptionenew RuntimeException("failed")new RuntimeException("DB query failed", e)"Failed to load user with id=" + userIdCauseSuppressedcausesuppressedcauseaddSuppressed()causesuppressedcausenew ServiceException("Validation failed", e)addSuppressed()Throwablesuper(message, cause)RuntimeExceptionRuntimeExceptionServiceExceptionValidationExceptionDataSourceException extends Exceptionthrows IOExceptionthrow new ServiceException(e)toString()errorCodeString message, Throwable causeint errorCodeString codeSerializable@ExceptionHandler@ExceptionHandlerRuntimeException@ExceptionHandler(RuntimeException.class)NullPointerException@ResponseStatus@ExceptionHandler(ServiceException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorResponse handleServiceException(ServiceException e) {
return new ErrorResponse(e.getErrorCode(), e.getMessage());
}new RuntimeException()