封装异常处理的核心是将技术异常转化为业务信号,通过分层自定义异常、统一工具封装、边界层翻译及全局处理器实现语义统一、变化隔离与重复减少。

封装异常处理逻辑不是给错误“套个壳”,而是让异常从底层技术细节,变成上层可理解、可决策、可响应的业务信号。核心是统一语义、隔离变化、减少重复。
用自定义异常明确表达错误意图
不要让调用方看到 IOException 就去猜是磁盘满了、网络断了,还是权限被拒。按故障性质分层定义异常:
- BusinessException(运行时):用户输入非法邮箱、订单已支付、库存不足——前端能直接提示,无需重试
- SystemException(建议继承 RuntimeException):Redis超时、MySQL连接池耗尽——触发降级或重试,不打断主流程
- FatalException(运行时):配置加载失败、核心类初始化异常——不应捕获,由监控系统捕获告警
这样写 catch (SystemException e),代码就自带容错意图,而不是靠注释解释“这里要重试”。
把通用处理逻辑抽成工具方法或模板
重复写 try-catch 做日志+默认值返回?不如封装一个安全转换工具:
立即学习“Java免费学习笔记(深入)”;
- 字符串转整数,返回
Optional<Integer>,不抛NumberFormatException - JSON 解析失败时,统一记录原始字符串、字段名、堆栈,并抛
InvalidDataException - HTTP 调用失败时,自动提取状态码、响应体,包装为
RemoteCallException并附带重试建议
这些方法在 service 层或 util 包里集中维护,一处修复,全局生效,避免各处“自己造轮子”式处理。
在边界层做异常翻译,不穿透技术细节
Controller 或 RPC 接口是系统对外的“门面”,不能把 SQLException 或 NullPointerException 直接透出:
- DTO 转 VO 时字段为 null?抛
IllegalArgumentException或填充默认值,别让空指针跑到前端 - 数据库查不到记录?根据场景返回
ResourceNotFoundException(404)或EmptyResultException(业务空数据) - @Valid 校验失败后仍 NPE?说明嵌套对象字段漏了
@NotNull,应在参数校验层拦截,而非在 service 里 try-catch
目标是:外部看到的异常,永远是它能理解的语言,比如“手机号格式错误”,而不是“java.lang.NumberFormatException: For input string: 'abc'”。
配合全局处理器,实现统一响应与日志
用 @ControllerAdvice + @ExceptionHandler 统一收口:
- 对
BusinessException返回 200 + 提示消息(前端友好) - 对
SystemException记录完整上下文(traceId、入参、SQL 片段脱敏)、返回 503 并触发熔断 - 所有异常日志必须含异常链(
e.getCause())、关键变量值、执行路径,避免只打e.printStackTrace()
注意多个 @ControllerAdvice 类处理同一异常类型时,Spring 按类名**字母序**选择处理器——命名要有规律,比如 GlobalExceptionHandler 和 WebExceptionHandler,避免第三方 SDK 的处理器意外生效。


















