Spring Boot全局异常处理失效的主因是@ControllerAdvice类未被Spring扫描到或@ExceptionHandler方法定义不合规;需确保其位于启动类同包或通过@ComponentScan指定,且方法为public非静态实例方法。

Spring Boot 全局异常处理“报错”本身不是异常,而是异常没被正确捕获或处理逻辑出错——最常见的情况是:你写了 @ControllerAdvice,但抛出自定义异常后仍返回 500 或默认错误页,而不是你预期的统一 JSON 响应。问题通常不在代码逻辑,而在 Spring 容器是否真正加载并启用了你的异常处理器。
检查 @ControllerAdvice 类是否被 Spring 扫描到
Spring Boot 默认只扫描启动类所在包及其子包。如果 GlobalExceptionHandler 放在 com.example.exception,而启动类在 com.example.app,且二者无父子关系,该类就不会注册为 Bean,整个异常处理器静默失效。
- 推荐做法:把异常处理器类放在启动类同包下(如
com.example.app.advice.GlobalExceptionHandler),无需额外配置 - 若必须跨包:在启动类上加
@ComponentScan(basePackages = {"com.example.app", "com.example.exception"}) - 验证方式:启动时加
--debug参数,查看日志中是否有Bean 'globalExceptionHandler' of type类似输出
确认 @ExceptionHandler 方法写法合规
Spring 要求这些方法必须是**非静态、public、实例方法**,且参数和返回值符合规范。任何偏差都会导致该方法被忽略。
- ❌ 错误示例:
public static ResponseEntity> handleApiException(...)—— static 修饰符直接让 Spring 忽略该方法 - ✅ 正确写法:
public ResponseEntity> handleApiException(ApiException e, HttpServletRequest req) - 注意:不要用
throws声明异常;方法内不要手动抛出新异常(除非你明确要触发下一级处理)
区分异常发生的位置:控制器 vs 过滤器/拦截器
@ControllerAdvice 只捕获从 DispatcherServlet 流程中抛出的异常,即控制器方法执行期间的异常。它对以下场景无效:
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
- 过滤器(Filter)中抛出的异常(如 JWT 解析失败、登录校验失败)
- 拦截器(HandlerInterceptor)
preHandle中抛出的异常 - 异步线程(
@Async)、定时任务(@Scheduled)中抛出的异常
这类异常需单独处理:过滤器中可手动调用你的异常处理器方法,或改用 Spring 的 ErrorController + 自定义 /error 映射兜底。
自定义异常类的设计建议
虽然语法允许继承 Exception(受检异常),但 REST API 场景下更推荐继承 RuntimeException:
- 避免强制 try-catch,符合 Web 层异常传播习惯
- 便于与 Spring 内置机制(如
@Valid抛出的MethodArgumentNotValidException)风格统一 - 建议包含字段:错误码(code)、提示信息(message)、HTTP 状态码(httpStatus)
不复杂但容易忽略

















