Spring Boot全局异常处理详解

发布于:2025-07-16 ⋅ 阅读:(17) ⋅ 点赞:(0)

原代码:

package com.weiyu.exception;

import com.weiyu.pojo.Result;
import com.weiyu.utils.ErrorFileResponseUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
@SuppressWarnings("unused") // 使用这个注解来抑制警告 或 使用 @Component
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理防抖异常(同时支持普通请求和文件下载请求)
     */
    @ExceptionHandler(DebounceException.class)
    public Object handleDebounceException(DebounceException e, HttpServletRequest request) {
        // 文件下载请求返回错误文件
        if (ErrorFileResponseUtils.isFileDownloadRequest(request)) {
            return ErrorFileResponseUtils.createErrorFileResponse(e);
        }

        // 2. 普通请求返回 json 错误
        return Result.error(e.getMessage(), e.getCode());
    }

    /**
     * 处理其他所有异常
     */
    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e, HttpServletRequest request) {

        // ❌ 禁止使用 printStackTrace 在控制台输出异常的详细堆栈跟踪信息
        // e.printStackTrace();
        // ✅ 规范做法:使用日志框架记录异常(参数 e 包含异常的详细堆栈跟踪信息)
        log.error("异常错误 {}", e.getMessage(), e);

        // 文件下载请求返回错误文件
        if (ErrorFileResponseUtils.isFileDownloadRequest(request)) {
            String message = StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "下载文件失败";
            return ErrorFileResponseUtils.createErrorFileResponse(message, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        // 普通请求返回 json 错误
        return Result.error(StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "操作失败", 500);
    }
}

以下是对提供的Spring Boot全局异常处理代码的详细注释和作用说明:

package com.weiyu.exception;

import com.weiyu.pojo.Result;                   // 自定义的统一响应对象
import com.weiyu.utils.ErrorFileResponseUtils;  // 处理错误文件响应的工具类
import jakarta.servlet.http.HttpServletRequest; // 获取HTTP请求信息
import lombok.extern.slf4j.Slf4j;               // Lombok日志注解
import org.springframework.http.HttpStatus;     // HTTP状态码枚举
import org.springframework.util.StringUtils;    // Spring字符串工具类
import org.springframework.web.bind.annotation.ExceptionHandler;        // 异常处理器注解
import org.springframework.web.bind.annotation.RestControllerAdvice;    // 控制器增强注解

/**
 * 全局异常处理器
 * 作用:集中处理整个应用程序中控制器层抛出的异常
 */
@RestControllerAdvice       // 组合注解:包含 @ControllerAdvice + @ResponseBody,使返回值自动转为JSON
@SuppressWarnings("unused") // 使用这个注解来抑制警告 或 使用 @Component
@Slf4j                      // 自动生成日志对象
public class GlobalExceptionHandler {

    /**
     * 处理防抖异常(DebounceException),同时支持普通请求和文件下载请求
     * 适用场景:当检测到重复/频繁请求时抛出的自定义异常
     *
     * @param e 捕获的防抖异常对象
     * @param request HTTP请求对象
     * @return 根据请求类型返回不同响应:文件下载请求返回错误文件,普通请求返回JSON错误信息
     */
    @ExceptionHandler(DebounceException.class)  // 指定处理的异常类型
    public Object handleDebounceException(DebounceException e, HttpServletRequest request) {
        // 1. 检查是否为文件下载请求
        if (ErrorFileResponseUtils.isFileDownloadRequest(request)) {
            // 生成包含错误信息的文件响应(如txt)
            return ErrorFileResponseUtils.createErrorFileResponse(e);
        }

        // 2. 普通请求返回统一JSON错误格式
        return Result.error(e.getMessage(), e.getCode());
    }

    /**
     * 处理所有其他未明确指定的异常(顶级异常处理器)
     * 作用:作为异常处理的兜底方案,确保所有异常都被处理
     */
    @ExceptionHandler(Exception.class)  // 捕获所有未被处理的异常
    public Object handleException(Exception e, HttpServletRequest request) {

        // ❌ 禁止使用 printStackTrace 在控制台输出异常的详细堆栈跟踪信息
        // e.printStackTrace();
        // ✅ 规范日志记录:使用日志框架记录完整异常堆栈(参数 e 包含异常的详细堆栈跟踪信息)
        log.error("异常错误 {}", e.getMessage(), e);    // 记录错误消息和详细堆栈跟踪信息

        // 1. 处理文件下载请求的异常
        if (ErrorFileResponseUtils.isFileDownloadRequest(request)) {
            // 确保错误消息不为空,使用默认消息兜底
            String message = StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "下载文件失败";
            // 生成包含错误信息的文件响应,使用500状态码
            return ErrorFileResponseUtils.createErrorFileResponse(message, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        // 2. 普通请求的异常处理
        return Result.error(
                StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "操作失败",    // 消息处理
                500 // 统一返回500服务器错误状态码
        );
    }
}

核心作用解析:

  1. 统一异常处理

    • 通过@RestControllerAdvice注解实现全局拦截控制器层抛出的异常

    • 避免在每个Controller中重复编写异常处理代码

  2. 差异化响应处理

  3. 自定义异常处理(DebounceException)

    • 专门处理防抖逻辑相关的异常

    • 使用异常自带的错误码和消息(e.getCode()e.getMessage()

    • 示例场景:限制用户频繁提交表单/重复请求

  4. 全局兜底处理(Exception.class)

    • 捕获所有未被特殊处理的异常

    • 关键安全实践:禁止使用printStackTrace()(会暴露敏感信息)

    • 正确做法:通过日志框架记录完整堆栈(log.error("msg", e)

  5. 空消息安全处理

    • 使用StringUtils.hasLength()检查消息有效性

    • 提供默认友好提示("操作失败"/"下载文件失败")

    • 避免返回空错误信息导致客户端解析失败

  6. HTTP状态码管理

    • 普通请求:返回500错误码(通过Result对象封装)

    • 文件请求:设置HTTP状态为500(HttpStatus.INTERNAL_SERVER_ERROR

最佳实践亮点:

  1. 日志规范

    java

    // 错误做法(禁止): 暴露堆栈到控制台
    // e.printStackTrace(); 
    
    // 正确做法(采用): 日志系统记录完整错误
    log.error("异常详细信息: {}", e.getMessage(), e);
  2. 扩展性设计

    • 通过ErrorFileResponseUtils工具类分离文件处理逻辑

    • 便于后续扩展其他文件类型处理

  3. 防御式编程

    • e.getMessage()进行空值检测

    • 使用工具类方法判断请求类型(isFileDownloadRequest

  4. 响应标准化

    • 统一使用Result对象返回JSON格式错误

    json

    // 响应示例
    {
      "code": 500,
      "msg": "操作失败",
      "data": null
    }

该实现有效解决了Spring Boot应用中的三个核心异常处理问题:
① 异常类型差异化处理 ② 响应形式自适配(JSON/文件) ③ 生产环境安全的错误日志记录


网站公告

今日签到

点亮在社区的每一天
去签到