Spring Boot全局异常处理应分层设计、封装响应、复用默认机制并完善日志。需定义BizException等子类精准捕获;统一返回ApiResponse对象;保留BasicErrorController;记录URI、方法及MDC上下文,过滤敏感信息。

Spring Boot 中全局异常处理本应简化代码、统一响应,但实践中常因设计不当反而引入新问题。避免这些反模式,关键在于分清职责、控制粒度、兼顾可读与安全。
❌ 反模式一:用 Exception.class 拦截一切,不区分异常类型
把所有异常都交给 @ExceptionHandler(Exception.class) 处理,看似“兜底”,实则丢失业务语义,掩盖真实问题。比如数据库连接失败和参数校验失败混为一谈,前端无法做差异化提示,日志也难定位根因。
- 明确划分异常层级:定义
BizException(业务异常)、ParamException(参数校验异常)、SystemException(系统级异常)等子类,继承自统一基类 - 每个
@ExceptionHandler方法只处理对应异常类型,例如:@ExceptionHandler(ParamException.class)返回 400 状态码 + 校验失败字段信息 - 保留
@ExceptionHandler(Exception.class)作为最后防线,仅记录完整堆栈并返回泛化错误(如“系统繁忙”),绝不暴露敏感细节
❌ 反模式二:在全局处理器里写重复逻辑,比如手动拼 JSON 或硬编码状态码
每次处理都 new Map、put 错误码和消息,或反复写 ResponseEntity.status(500).body(...),既冗余又易错,还违背响应体统一规范。
- 封装标准响应结构,如
ApiResponse<t></t>,含code、msg、data字段,并提供静态工厂方法(ApiResponse.success()、ApiResponse.error(404, "xxx")) - 全局异常处理器中直接返回
ResponseEntity.ok(apiResponse)或ResponseEntity.status(status).body(apiResponse),复用同一套序列化逻辑 - 状态码与业务错误码解耦:HTTP 状态码反映通信/协议层问题(400/404/500),业务错误码(如 "USER-001")放在响应体内部,供前端判断具体原因
❌ 反模式三:忽略 Spring Boot 默认异常机制,重复造轮子
完全弃用 /error 端点,自己实现所有错误页面或 JSON 响应,导致 404、405、503 等非业务异常无法被统一管理,甚至破坏 Actuator 的健康检查行为。
- 保留默认 BasicErrorController,仅通过
@ControllerAdvice补充业务异常处理,不覆盖默认错误路径 - 若需定制错误页,优先覆盖
src/main/resources/templates/error/404.html等 Thymeleaf 模板,而非重写控制器 - 对 REST API 场景,确保
produces = MediaType.APPLICATION_JSON_VALUE的异常处理器生效,避免 HTML 和 JSON 响应混杂
❌ 反模式四:日志记录不充分或过度,缺乏请求上下文
只记 e.getMessage(),或无差别打印全堆栈,却漏掉关键线索——比如哪个接口、什么参数、用户 ID 是多少,导致排查时反复问复现步骤。
- 在
@ExceptionHandler方法中,通过HttpServletRequest获取getRequestURI()、getMethod(),必要时解析请求体或 header(如 X-Request-ID) - 使用结构化日志(如 Logback 的
%X{traceId})关联链路,或手动注入 MDC:MDC.put("uri", request.getRequestURI()) - 对敏感异常(如数据库密码泄露风险),过滤堆栈中的连接字符串、密钥等字段,再记录


















