什么是 @ControllerAdvice 与 @RestControllerAdvice 之间的区别?
Source: Dev.to
了解 Spring Boot 中 @ControllerAdvice 与 @RestControllerAdvice 的区别,提供清晰的解释和完整的 Java 示例。
介绍
想象你正在使用 Spring Boot 构建两个应用:
- 传统 Web 应用 – 在浏览器中显示 HTML 页面。
- REST API – 被移动应用或像 React 这样的前端框架消费。
现在假设出现了错误——比如用户未找到或请求无效。
你想在 每个控制器内部 处理错误吗?显然不想。
Spring 提供了一种简洁的方式来 全局 处理错误,即使用 @ControllerAdvice 和 @RestControllerAdvice。
初学者常问:
- 这两个注解是一样的吗?
- 什么时候该使用哪一个?
- 为什么 REST API 的行为会不同?
在本博客中,我们将通过 清晰的概念和端到端的完整示例,解释 @ControllerAdvice 与 @RestControllerAdvice 的区别,帮助你在实际的 Spring Boot 项目中自信地使用它们。
Source: …
核心概念
什么是 @ControllerAdvice?
@ControllerAdvice 用于在 控制器之间全局处理异常。可以把它看作是应用程序的 统一错误处理器。
简易理解方式
与其让每个控制器各自处理错误,不如让一个统一的管理器为所有控制器处理。
关键点
- 与
@Controller(MVC 应用)配合使用 - 通常返回 HTML 视图 或
ModelAndView - 若想返回 JSON,需要配合
@ResponseBody
使用场景
- Web 应用
- 服务器端渲染页面(Thymeleaf、JSP)
- 自定义错误页面
什么是 @RestControllerAdvice?
@RestControllerAdvice 专为 REST API 设计。技术上相当于:
@ControllerAdvice + @ResponseBody
简易理解方式
与其返回错误页面,它返回 结构化的 JSON 数据。
关键点
- 与
@RestController配合使用效果最佳 - 自动将响应转换为 JSON
- 适用于微服务和 API
使用场景
- REST API
- 移动端或前端驱动的应用
- 微服务架构
一目了然的关键区别
| 功能 | @ControllerAdvice | @RestControllerAdvice |
|---|---|---|
| 应用类型 | MVC / Web 应用 | REST API |
| 默认响应 | HTML / 视图 | JSON |
是否需要 @ResponseBody | 是 | 否 |
| 最适合 | 基于 UI 的应用 | API 与微服务 |
Source:
端到端代码示例(Java 21)
以下是 完整可运行的示例,展示了每个注解在实际场景中的使用方式。
示例 1:使用 @ControllerAdvice 的端到端 MVC 流程
步骤 1 – 自定义异常
public class PageNotFoundException extends RuntimeException {
public PageNotFoundException(String message) {
super(message);
}
}
步骤 2 – MVC 控制器
@Controller
@RequestMapping("/pages")
public class PageController {
@GetMapping("/{id}")
public String getPage(@PathVariable int id) {
if (id != 1) {
throw new PageNotFoundException("Page not found with id: " + id);
}
return "home"; // returns an HTML page
}
}
步骤 3 – 使用 @ControllerAdvice 的全局异常处理器
@ControllerAdvice
public class GlobalMvcExceptionHandler {
@ExceptionHandler(PageNotFoundException.class)
public String handlePageNotFound(PageNotFoundException ex, Model model) {
model.addAttribute("errorMessage", ex.getMessage());
return "error"; // resolves to error.html
}
}
结果: 浏览器收到 HTML 错误页面 —— 实现了集中式错误处理,同时保持控制器代码简洁。
示例 2:使用 @RestControllerAdvice 的端到端 REST API 流程
步骤 1 – 自定义异常
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
步骤 2 – 错误响应模型
import java.time.LocalDateTime;
public record ErrorResponse(
int status,
String message,
LocalDateTime timestamp
) {}
步骤 3 – REST 控制器
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public String getUser(@PathVariable Long id) {
if (!id.equals(1L)) {
throw new ResourceNotFoundException("User not found with id: " + id);
}
return "User found";
}
}
步骤 4 – 使用 @RestControllerAdvice 的全局异常处理器
@RestControllerAdvice
public class GlobalRestExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity handleResourceNotFound(
ResourceNotFoundException ex) {
ErrorResponse response = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
}
}
结果(JSON 响应):
{
"status": 404,
"message": "User not found with id: 2",
"timestamp": "2025-01-10T12:30:45"
}
Best Practices
- 使用
@RestControllerAdvice处理 REST API – 保证自动序列化(例如 JSON),并避免遗漏@ResponseBody。 - 仅在 MVC 应用中使用
@ControllerAdvice– 当需要返回视图或模板时最为合适。 - 创建一致的错误响应结构 – 有助于前端团队和 API 使用者统一处理错误。
- 映射正确的 HTTP 状态码 – 对每种错误情况适当地使用
400、404、401、500等。 - 避免首先使用通用
Exception处理 – 应始终先处理具体异常,再回退到Exception.class。
常见错误需避免
- ❌ 在 REST API 中使用
@ControllerAdvice而未加@ResponseBody。 - ❌ 从 REST API 返回 HTML 错误页面。
- ❌ 在各个控制器内部处理异常,而不是集中处理。
- ❌ 在 API 响应中暴露堆栈跟踪或内部细节。
结论
@ControllerAdvice 与 @RestControllerAdvice 的区别在于 应用程序如何传递错误信息:
- 对于返回 HTML 的传统 Web 应用,使用
@ControllerAdvice。 - 对于返回 JSON(或其他基于响应体的格式)的 REST API,使用
@RestControllerAdvice。
这两个注解都提供了集中、简洁且可扩展的异常处理方式。理解它们的区别可以让你的 Spring Boot 项目更易于维护,也更显专业——无论是在实际项目中还是面试时。
