全局异常处理器(Global Exception Handler)

在 Spring Boot 框架中,全局异常处理器(Global Exception Handler) 是一种集中处理应用程序中未被捕获的异常的机制。它基于 Spring MVC 的 @ControllerAdvice@ExceptionHandler 注解实现,能够统一返回格式、提升系统健壮性与用户体验。


一、核心原理

1. 底层机制

  • Spring MVC 在处理请求时,若 Controller 方法抛出异常且未被 try-catch 捕获,会将异常交给 HandlerExceptionResolver 处理。
  • 默认的实现类 ExceptionHandlerExceptionResolver 会扫描带有 @ControllerAdvice 的类,并调用其中匹配的 @ExceptionHandler 方法。
  • 最终将异常转换为统一的 HTTP 响应(如 JSON 格式),避免暴露堆栈信息给前端。

2. 关键注解

注解 作用
@ControllerAdvice 定义一个全局的异常处理类,作用于所有 @Controller@RestController
@RestControllerAdvice = @ControllerAdvice + @ResponseBody,直接返回 JSON(推荐用于 REST API)
@ExceptionHandler 标注在方法上,指定该方法处理哪些异常类型

二、使用方法(完整示例)

步骤 1:定义统一响应格式(可选但推荐)

复制代码
// 统一返回结构
public class Result<T> {
    private int code;
    private String message;
    private T data;

    public static <T> Result<T> error(int code, String message) {
        Result<T> result = new Result<>();
        result.code = code;
        result.message = message;
        return result;
    }

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.code = 200;
        result.message = "success";
        result.data = data;
        return result;
    }

    // getter/setter 省略
}

步骤 2:创建全局异常处理器

复制代码
import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;

@RestControllerAdvice // 全局异常处理 + 自动@ResponseBody
public class GlobalExceptionHandler {

    // 1. 处理自定义业务异常
    @ExceptionHandler(BusinessException.class)
    public Result<String> handleBusinessException(BusinessException e) {
        return Result.error(e.getCode(), e.getMessage());
    }

    // 2. 处理参数校验失败(JSR-303 / @Valid)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<String> handleValidationException(MethodArgumentNotValidException e) {
        StringBuilder sb = new StringBuilder();
        for (FieldError error : e.getBindingResult().getFieldErrors()) {
            sb.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("; ");
        }
        return Result.error(400, "参数校验失败: " + sb.toString());
    }

    // 3. 处理空指针异常(开发阶段常见)
    @ExceptionHandler(NullPointerException.class)
    public Result<String> handleNPE(NullPointerException e) {
        // 生产环境不建议暴露具体错误
        return Result.error(500, "服务器内部错误");
    }

    // 4. 处理通用异常(兜底)
    @ExceptionHandler(Exception.class)
    public Result<String> handleGenericException(Exception e) {
        e.printStackTrace(); // 记录日志(实际应使用 SLF4J)
        return Result.error(500, "系统繁忙,请稍后再试");
    }

    // 5. 处理特定 HTTP 状态码异常(如 404)
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<String> handle404(NoHandlerFoundException e) {
        return Result.error(404, "接口不存在");
    }
}

步骤 3:定义自定义业务异常(可选)

复制代码
public class BusinessException extends RuntimeException {
    private int code;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    // getter
    public int getCode() { return code; }
}

步骤 4:在 Controller 中抛出异常

复制代码
@RestController
@RequestMapping("/api/user")
public class UserController {

    @PostMapping("/register")
    public Result<String> register(@Valid @RequestBody UserDTO user) {
        if ("admin".equals(user.getUsername())) {
            throw new BusinessException(1001, "用户名已被禁止注册");
        }
        // ... 业务逻辑
        return Result.success("注册成功");
    }
}

步骤 5:启用 Spring Boot 对 404 的异常处理(重要!)

application.yml 中添加:

复制代码
spring:
  mvc:
    throw-exception-if-no-handler-found: true
  web:
    resources:
      add-mappings: false  # 禁用静态资源处理,让 404 走异常处理器

否则 NoHandlerFoundException 不会触发。


三、执行流程图解

复制代码
[HTTP Request]
       ↓
[Controller Method] → 抛出异常(如 BusinessException)
       ↓
Spring MVC 捕获异常
       ↓
查找 @ControllerAdvice 中匹配的 @ExceptionHandler
       ↓
执行 handleBusinessException()
       ↓
返回 Result 对象 → 自动序列化为 JSON
       ↓
[HTTP Response]
{
  "code": 1001,
  "message": "用户名已被禁止注册",
  "data": null
}

四、注意事项

  1. 优先级
    @ExceptionHandler 方法越具体(如 BusinessException),优先级越高;Exception.class 是兜底。

  2. 日志记录

    handleGenericException 中应使用 log.error("系统异常", e); 而非 printStackTrace()

  3. 生产安全

    切勿将 e.getMessage() 或堆栈直接返回给前端,防止信息泄露。

  4. 异步支持

    若使用 @Async,全局异常处理器不会生效 ,需单独处理(如实现 AsyncUncaughtExceptionHandler)。

  5. WebFlux(响应式)

    上述基于 Servlet,若用 WebFlux 需使用 @ControllerAdvice + WebExceptionHandler


五、总结

优势 说明
✅ 统一错误格式 前端无需处理各种异常结构
✅ 解耦业务逻辑 Controller 只关注业务,不写 try-catch
✅ 提升可维护性 异常处理集中管理
✅ 增强安全性 避免敏感信息泄露

通过全局异常处理器,你的 Spring Boot 应用将具备更专业、稳定的 API 行为。建议结合 自定义异常 + 参数校验 + 日志监控 构建完整异常体系。

相关推荐
晴天sir13 天前
关于使用poi-tl读取本地图片,转为base64编码批量插入word的解决方法
java·exception·poi-tl
全粘架构师20 天前
五分钟精通RuntimeException
java·exception
linksinke1 个月前
Mapstruct引发的 Caused by: java.lang.NumberFormatException: For input string: ““
java·开发语言·exception·mapstruct·numberformat·不能为空
CinzWS1 个月前
Cortex-R52+ 架构深度解析与国产芯片实战
arm·exception·coretex-r52+·aarch32
CinzWS1 个月前
RISC-V RV32MCU 架构、启动与运行机制深度剖析
risc-v·exception
邂逅星河浪漫2 个月前
【Java】异常详解+实例演示+知识总结
java·异常·exception
Tipriest_4 个月前
C++关于手动抛出异常,自动抛出异常以及一些不会抛出异常的情况说明
开发语言·c++·异常·exception
SuperherRo6 个月前
Web攻防-PHP反序列化&原生内置类&Exception类&SoapClient类&SimpleXMLElement
php·xss·反序列化·exception·ssrf·原生类·soapclient
ssxueyi1 年前
Java 常见Exception异常解决方法
java·开发语言·数据库·异常处理·exception