在 Spring Boot 中配置全局异常处理器,是实现统一错误响应、提升系统健壮性和可维护性的关键手段。核心使用两个注解:@ControllerAdvice 和 @ExceptionHandler。
下面从 原理、步骤、完整示例、最佳实践 四个方面详细说明。
@ControllerAdvice
是一个组合注解(包含 @Component),作用于类上,表示该类是一个全局的控制器增强器。它会自动被 Spring 容器扫描并注册为 Bean,并对所有 @Controller 或 @RestController 中抛出的异常进行拦截。
@ExceptionHandler
作用于方法上,用于指定该方法处理哪些类型的异常。可以精确到具体异常类(如 NullPointerException),也可以处理通用异常(如 Exception)。
💡 执行流程: Controller 抛出异常 → Spring MVC 的
DispatcherServlet捕获 → 查找匹配的@ControllerAdvice中的@ExceptionHandler方法 → 执行并返回结果。
package com.example.demo.exception;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
// @RestControllerAdvice = @ControllerAdvice + @ResponseBody
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理特定异常
@ExceptionHandler(NullPointerException.class)
public Result handleNpe(NullPointerException e) {
return Result.error("空指针异常: " + e.getMessage());
}
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public Result handleBusiness(BusinessException e) {
return Result.error(e.getCode(), e.getMessage());
}
// 兜底处理所有未捕获异常
@ExceptionHandler(Exception.class)
public Result handleGeneral(Exception e) {
// 生产环境建议不暴露具体异常信息
return Result.error(500, "服务器内部错误");
}
}🔔 注意:
@RestControllerAdvice 而不是 @ControllerAdvice,是为了让返回值自动转为 JSON(相当于每个方法都加了 @ResponseBody)。@ControllerAdvice。package com.example.demo.common;
import lombok.Data;
@Data
public class Result<T> {
private int code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.code = 200;
result.message = "success";
result.data = data;
return result;
}
public static <T> Result<T> error(String message) {
return error(500, message);
}
public static <T> Result<T> error(int code, String message) {
Result<T> result = new Result<>();
result.code = code;
result.message = message;
return result;
}
}package com.example.demo.exception;
public class BusinessException extends RuntimeException {
private int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
// getter...
}在业务代码中抛出:
if (user == null) {
throw new BusinessException(1001, "用户不存在");
}@SpringBootApplication(scanBasePackages = "com.example")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}默认情况下,访问不存在的 URL 不会抛出异常,而是返回 404 页面。若想让 404 也被全局处理器捕获,需在 application.properties 中添加:
# 当没有找到处理器时,抛出 NoHandlerFoundException
spring.mvc.throw-exception-if-no-handler-found=true
# 禁用静态资源映射(避免干扰)
spring.web.resources.add-mappings=false然后在全局处理器中添加:
@ExceptionHandler(NoHandlerFoundException.class)
public Result handle404() {
return Result.error(404, "接口不存在");
}src/main/java/com/example/demo/
├── DemoApplication.java
├── controller/
│ └── UserController.java
├── exception/
│ ├── GlobalExceptionHandler.java
│ └── BusinessException.java
└── common/
└── Result.java场景 | 建议 |
|---|---|
开发阶段 | 可在 handleGeneral 中返回 e.getMessage() 便于调试 |
生产环境 | 隐藏具体异常信息,只返回友好提示,避免信息泄露 |
异常分类 | 优先处理具体异常(如 ValidationException, IllegalArgumentException),最后用 Exception 兜底 |
日志记录 | 在 @ExceptionHandler 方法中加入 log.error("异常:", e); |
HTTP 状态码 | 可结合 ResponseEntity 返回不同状态码:return ResponseEntity.status(400).body(Result.error(...)); |
spring-boot-starter-web 依赖spring.mvc.throw-exception-if-no-handler-found=true@RestControllerAdvice,而不是 @ControllerAdvice通过 @RestControllerAdvice + @ExceptionHandler,你可以:
这是 Spring Boot 项目必备的基础配置之一。