@ControllerAdvice 和 @ExceptionHandler 仅处理 Spring MVC 层未被捕获的异常,不覆盖 Filter/Servlet 层;常见失效原因是类未被扫描到、不在主启动类包或子包下、未正确标注注解或异常类型不匹配。

ControllerAdvice 和 @ExceptionHandler 不是“自动兜底”,它们只对当前 Spring MVC 请求生命周期内的、未被其他 handler 捕获的异常生效;抛到 Filter 或 Servlet 层的异常,它们管不了。
为什么 @ControllerAdvice 类没生效?
最常见的原因是类没被 Spring 扫描到,或者扫描路径没覆盖它。Spring Boot 默认只扫 @SpringBootApplication 注解所在包及子包,如果你把 @ControllerAdvice 类放在外面,它就只是个普通类。
- 确认该类在主启动类的包或其子包下,否则得手动加
@ComponentScan指定路径 - 确保类上有
@ControllerAdvice(不是@RestControllerAdvice也能用,但返回 JSON 更方便) - 如果用了
@Order,注意值越小优先级越高;多个 advice 冲突时,低序号的先执行 - 它默认只拦截
@Controller和@RestController方法抛出的异常,不处理@Scheduled或异步线程里的异常
@ExceptionHandler 只捕获特定异常,但实际抛的是包装异常
比如数据库操作抛出 SQLException,但 MyBatis 通常会把它封装成 org.apache.ibatis.exceptions.PersistenceException,再往上可能又被 Spring 的 DataAccessException 包一层。你写 @ExceptionHandler(SQLException.class) 就完全捕不到。
- 优先捕获 Spring 的抽象异常类型,比如
DataAccessException、HttpRequestMethodNotSupportedException - 用
@ExceptionHandler(Exception.class)做兜底可以,但别放最上面——它会吃掉所有更具体的 handler - 如果必须捕原始异常,记得检查实际栈顶异常类型,用
e.getCause()往下挖两层很常见
返回值怎么设才不影响全局状态码和响应体?
直接 return 字符串或 Map,Spring 会按默认规则转 JSON,但 HTTP 状态码默认是 200。用户看到 {"code":500,"msg":"xxx"} 却收到 200,前端判断就乱了。
立即学习“Java免费学习笔记(深入)”;
- 用
ResponseEntity<?>显式控制状态码:return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result) - 或者在方法上加
@ResponseStatus(HttpStatus.NOT_FOUND),适合单一异常类型 - 避免在
@ExceptionHandler方法里 throw 新异常——除非你明确想触发下一级 handler,否则就是给自己埋递归陷阱 - 如果用了
@RestControllerAdvice,返回对象会自动 JSON 序列化;但若混用@ControllerAdvice+@ResponseBody,别漏掉后者
真正麻烦的从来不是写几个注解,而是异常从哪来、经过了哪些拦截器、有没有被提前吞掉、HTTP 状态码和业务 code 是否一致——这些地方一错,日志里看着有异常,前端却收不到错误信号。










