@restcontrolleradvice 更适合 rest api,因其默认添加 @responsebody,避免手动标注或视图解析错误,确保统一返回 json;而 @controlleradvice 可能导致空白页或序列化失败。

@RestControllerAdvice 为什么比 @ControllerAdvice 更适合 REST API
因为 @RestControllerAdvice 默认给所有处理方法加了 @ResponseBody,返回 JSON 时不用每个方法都写 @ResponseBody 或让类继承 @RestController。如果用 @ControllerAdvice,遇到 @GetMapping 返回对象,可能悄悄走视图解析(比如跳 404 页面),而不是返回 JSON 错误体。
常见错误现象:RestResponseEntityExceptionHandler 生效但返回的是空白页或 HTML 错误页,不是预期的 JSON;或者日志里出现 No converter found for return value of type。
- 只对
@RestController和@ResponseBody方法生效 —— 这是它的设计前提 - 如果项目混用 MVC 视图和 REST 接口,别强行统一用一个
@RestControllerAdvice,拆开更稳 - 它本身不处理 404、405 这类容器级错误(比如路径不存在、HTTP 方法不支持),那些得靠
ErrorWebExceptionHandler(WebFlux)或自定义BasicErrorController(MVC)
捕获异常时,为什么顺序和继承关系比 try-catch 还关键
Spring 按照异常类型从具体到宽泛匹配 @ExceptionHandler 方法。写两个方法分别处理 IllegalArgumentException 和 RuntimeException,但把 RuntimeException 的方法放在前面,那 IllegalArgumentException 永远进不去——父类先匹配上了。
使用场景:你想对数据库唯一约束失败(DuplicateKeyException)返回 409,对其他 SQL 异常返回 500,就必须把前者声明在后者之前。
- 同一个
@RestControllerAdvice类里,方法顺序无关,Spring 只看参数类型 - 多个
@RestControllerAdvice类之间,可通过@Order控制优先级,数值越小越靠前 - 不要依赖
Exception通配兜底来记录日志——它会吞掉你原本想抛给上层的业务异常,建议用Throwable或单独加一层日志切面
@ExceptionHandler 方法返回值怎么选:ResponseEntity vs 自定义响应体
直接返回 ResponseEntity<apiresult></apiresult> 最灵活,能控制状态码、Header、Body 三者;但如果全项目统一结构,定义一个 ApiResult<t></t> 并让所有 @ExceptionHandler 方法返回它,反而更易维护。
性能影响:用 ResponseEntity 多一次包装,但几乎可忽略;真正要注意的是别在异常处理器里做耗时操作(如远程调用、复杂计算),否则拖慢整个错误响应。
- 返回
String或普通对象 → 会被 Jackson 序列化,但状态码固定为 200,除非显式设response.setStatus(400) - 返回
ResponseEntity→ 状态码、Header、Body 全可控,推荐用于需要精确控制 HTTP 行为的场景(如 302 重定向、429 限流头) - 避免在
@ExceptionHandler里 throw 新异常——可能触发二次拦截,导致状态码错乱或循环调用
全局异常拦截器失效的三个典型原因
最常被忽略的是:你的异常根本没抛到 Spring MVC 的调用链里。比如在 @Async 方法、定时任务 @Scheduled、或 Filter 中抛出异常,@RestControllerAdvice 压根收不到。
另一个隐形坑:自定义了 WebMvcConfigurer 并重写了 extendHandlerExceptionResolvers,但没把默认的 ExceptionHandlerExceptionResolver 加回去,等于自己干掉了全局异常处理能力。
- 异步方法异常 → 用
@Async的TaskExecutor配置setThreadFactory+UncaughtExceptionHandler,或改用CompletableFuture显式 catch - Filter / Interceptor 抛异常 → 它们在 DispatcherServlet 外层,需在 Filter 中 try-catch 后手动写响应,或用
OncePerRequestFilter包裹 - Controller 方法签名含
throws Exception→ Spring 不会自动包装成Exception,而是当受检异常处理,必须显式 throw 才进@ExceptionHandler
@ExceptionHandler,而是想清楚异常该在哪一层截、哪一层透、哪一层转成用户能懂的话——这得贴着业务逻辑去判,不是靠注解堆出来的。










