@exceptionhandler需配合@controlleradvice才能全局捕获异常,其作用是声明处理哪类异常而非自动拦截;局部在@controller内生效,全局需@restcontrolleradvice注册为bean,且异常类型须匹配、无更高优先级拦截。

@ExceptionHandler 本身不能单独“捕获”异常,它必须配合 @ControllerAdvice(或 @RestControllerAdvice)使用,才能实现真正的全局捕获。它的作用是声明“**这个方法负责处理哪一类异常**”,而不是自动监听或拦截。
明确作用范围:局部 or 全局?
• 如果只在某个 @Controller 内使用 @ExceptionHandler,那它只对该控制器里的方法生效;
• 如果放在被 @RestControllerAdvice 标记的类里,它就对整个项目所有 @RestController 生效;
• @RestControllerAdvice = @ControllerAdvice + @ResponseBody,返回值直接转为 JSON,适合前后端分离项目。
怎么写才真正捕获到异常?
关键不是加注解,而是让异常“走到它面前”。Spring MVC 的 DispatcherServlet 在 Controller 方法抛出异常后,会主动查找匹配的 @ExceptionHandler 方法——前提是:
• 异常类型与注解中指定的类完全匹配或为其子类;
• 该方法所在类已被 Spring 扫描并注册为 Bean(靠 @RestControllerAdvice 实现);
• 没有更优先的局部 @ExceptionHandler 拦截了它(局部优先级高于全局)。
常见校验异常怎么对应?
参数校验失败时,Spring 会抛出不同异常,需按类型精准拦截:
• @Valid + @RequestBody 失败 → 抛 MethodArgumentNotValidException;
• @Valid + 普通对象参数(非 Body)失败 → 抛 BindException;
• 手动 throw new IllegalArgumentException(...) → 直接捕获 IllegalArgumentException;
• 运行时异常如 NullPointerException、RuntimeException,可单独写或用 Exception.class 兜底(生产环境建议慎用兜底,避免掩盖问题)。
一个可靠写法示例
不推荐只写 @ExceptionHandler(Exception.class) 一把抓,而应分层处理:
• 先处理已知业务异常(如 BusinessException);
• 再处理校验异常(MethodArgumentNotValidException),从中提取字段错误信息;
• 最后用 @ExceptionHandler(Exception.class) 做保底,返回统一 500 并记录日志,但不暴露堆栈。











