Spring Boot 全局异常处理器(GlobalExceptionHandler)

Spring Boot 全局异常处理器(GlobalExceptionHandler) 的核心代码,是和 CommonResult<T> 配套的后端统一异常处理方案,每一行代码的作用 ,以及为什么要单独写这个类
This is the core code of the Spring Boot GlobalExceptionHandler, which is part of the "backend unified exception handling" solution paired with CommonResult<T> . Below, I will explain the function of each line of code, as well as why this class is written separately.


一、代码逐行拆解

1. 核心注解与类结构

复制代码
// 全局异常处理器注解,标记这是一个全局异常处理类
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 注入日志对象,用于打印异常栈
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    // 1. 处理 Controller 层自定义异常(示例)
    @ExceptionHandler(value = ControllerException.class)
    public CommonResult<?> controllerException(ControllerException e) {
        // 打印错误日志,方便排查问题
        logger.error("controllerException:", e);
        // 封装统一错误响应,返回给前端
        return CommonResult.error(
            GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),
            e.getMessage()
        );
    }

    // 2. 处理所有未捕获的 Exception 异常(兜底)
    @ExceptionHandler(value = Exception.class)
    public CommonResult<?> exception(Exception e) {
        // 打印服务异常日志,包含完整异常栈
        logger.error("服务异常:", e);
        // 封装统一错误响应,返回给前端
        return CommonResult.error(
            GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),
            e.getMessage()
        );
    }
}

2. 关键注解说明

注解 作用
@RestControllerAdvice 标记类为全局异常处理器 ,会拦截所有 @RestController 抛出的未捕获异常,是全局异常处理的核心注解
@ExceptionHandler(value = 异常类.class) 标记方法用于处理指定类型的异常,value = Exception.class 表示捕获所有未被其他方法处理的异常(兜底)

3. 核心逻辑拆解

  1. 日志打印logger.error("xxx:", e)

    • 作用:把完整的异常栈信息打印到日志文件中,方便开发 / 运维排查线上问题
    • 注意:这里必须传 e(异常对象),才能打印完整的异常栈,只传 e.getMessage() 只会打印错误信息,无法定位问题
  2. 统一响应封装CommonResult.error(...)

    • 作用:把异常封装成和 CommonResult<T> 完全一致的格式返回给前端
    • 结构:{ "code": 500, "msg": "xxx错误信息", "data": null }
    • 优势:前端不用再处理「接口抛异常返回 500 错误页」的情况,永远收到统一格式的响应
  3. 错误码常量GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR

    • 作用:统一管理错误码,避免魔法值(比如直接写 500),保证全项目错误码一致
    • 示例:INTERNAL_SERVER_ERROR 对应 code=500msg="服务器内部错误"

二、为什么要单独写这个类?(核心设计意义)

如果不写这个全局异常处理器,会出现什么问题?

1. 原生 Spring Boot 异常返回的痛点

  • 接口失败时,前端收到的是「错误页 / 500 状态码」,不是统一格式的 JSON 比如:Whitelabel Error Page 页面,前端无法解析,直接报错
  • 不同异常的返回结构五花八门,前端要写 N 套错误处理逻辑
  • 异常信息直接暴露给用户 :比如 NullPointerExceptionSQL 语法错误 等敏感信息,有安全风险
  • 业务异常和系统异常混在一起,排查困难

2. 全局异常处理器的核心价值

✅ 1. 统一异常返回格式,和 CommonResult 完美适配

所有异常(不管是业务异常、系统异常)都被封装成 CommonResult.error(),前端永远收到统一格式的 JSON,一套逻辑处理所有错误,彻底解决前后端联调的痛点。

✅ 2. 业务代码零侵入,专注业务逻辑

Controller/Service 层只需要直接抛异常 ,不用手动写 try-catch 封装 CommonResult.error(),业务代码更干净、更易维护。

复制代码
// 业务代码中,直接抛异常即可,不用手动处理返回
if (user == null) {
    throw new BusinessException("用户不存在");
}
✅ 3. 敏感信息隐藏,提升系统安全性

可以自定义异常处理逻辑,不把底层异常栈(如 SQL 错误、空指针)直接返回给用户,只返回友好的提示信息,避免泄露系统细节。

复制代码
// 优化示例:隐藏底层异常,返回统一提示
return CommonResult.error(
    GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),
    "服务器繁忙,请稍后再试"
);
✅ 4. 统一日志管理,便于问题排查

所有异常都在这个类中统一打印日志,不用在每个 Controller 中手动写日志,日志格式统一,排查问题更高效。

✅ 5. 分层异常分类处理,可扩展性极强

可以针对不同类型的异常,写不同的处理逻辑:

  • 业务异常(BusinessException):返回业务错误码(如 400 = 参数错误)
  • 权限异常(UnauthorizedException):返回 401 未授权
  • 系统异常(Exception):返回 500 服务器错误
  • 限流异常、熔断异常等:单独处理,返回对应错误码

三、完整优化版代码(生产环境可直接用)

复制代码
package com.example.lotterysystem.controller.handler;

import com.example.lotterysystem.common.errorcode.GlobalErrorCodeConstants;
import com.example.lotterysystem.common.exception.BusinessException;
import com.example.lotterysystem.common.pojo.CommonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

/**
 * 全局异常处理器
 * 统一处理所有Controller抛出的异常,封装为CommonResult返回
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理业务自定义异常(主动抛出的业务错误)
     */
    @ExceptionHandler(value = BusinessException.class)
    public CommonResult<?> businessException(BusinessException e) {
        logger.error("业务异常: {}", e.getMessage(), e);
        return CommonResult.error(e.getCode(), e.getMessage());
    }

    /**
     * 处理参数校验异常(@Valid 注解校验失败)
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public CommonResult<?> constraintViolationException(ConstraintViolationException e) {
        logger.error("参数校验异常: {}", e.getMessage(), e);
        // 提取第一个校验错误信息
        String msg = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .findFirst()
                .orElse("参数错误");
        return CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), msg);
    }

    /**
     * 处理所有未捕获的系统异常(兜底)
     */
    @ExceptionHandler(value = Exception.class)
    public CommonResult<?> exception(Exception e) {
        logger.error("服务异常: ", e);
        // 生产环境隐藏底层异常,返回统一提示,避免泄露敏感信息
        return CommonResult.error(
                GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),
                "服务器繁忙,请稍后再试"
        );
    }
}

四、和 CommonResult<T> 的联动流程(完整闭环)

复制代码
前端发起请求
    ↓
Controller 接收参数
    ↓
Service 执行业务逻辑
    ↓
    业务执行结果
    /      \
成功      失败/抛异常
↓          ↓
CommonResult.success()  →  全局异常处理器捕获异常
                          ↓
                          CommonResult.error()
                          ↓
SpringBoot 序列化为统一JSON
                          ↓
返回给前端统一格式响应

五、关键优化建议(生产环境必做)

  1. 禁止直接返回 e.getMessage() 给前端 生产环境中,系统异常(如空指针、SQL 错误)的 getMessage() 会泄露系统细节,必须替换为统一提示(如「服务器繁忙,请稍后再试」),只在日志中打印完整异常栈。

  2. 自定义业务异常类 单独定义 BusinessException 继承 RuntimeException,用于主动抛出业务错误,和系统异常区分开,方便分类处理。

  3. 错误码枚举化 所有错误码都定义在 GlobalErrorCodeConstants 枚举中,避免魔法值,保证全项目错误码一致。

  4. 参数校验异常单独处理 针对 @Valid 注解的参数校验异常,单独提取错误信息,返回友好提示,提升用户体验。

相关推荐
__土块__2 小时前
大厂后端一面模拟:从线程安全到分布式缓存的连环追问
jvm·redis·mysql·spring·java面试·concurrenthashmap·大厂后端
陌殇殇2 小时前
002 Spring AI Alibaba框架整合百炼大模型平台 — 聊天、文生图、语音、向量模型整合
人工智能·spring·ai
随风,奔跑2 小时前
Spring Security
java·后端·spring
han_hanker2 小时前
@GetMapping @PostMapping @DeleteMapping @PutMapping
spring boot
Java成神之路-3 小时前
Spring 事务从入门到精通:一篇搞定事务失效、传播行为、回滚规则(Spring系列10)
spring
卓怡学长3 小时前
m326数据结构课程网络学习平台的设计与实现+vue
java·spring·tomcat·maven·intellij-idea·mybatis
han_hanker3 小时前
@Validated @Valid 用法
java·spring boot
言慢行善3 小时前
SpringBoot中的注解介绍
java·spring boot·后端
身如柳絮随风扬4 小时前
SpringMVC 异常处理?Spring 父子容器?
java·spring·mvc