原代码:
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服务器错误状态码
);
}
}
核心作用解析:
统一异常处理
通过
@RestControllerAdvice
注解实现全局拦截控制器层抛出的异常避免在每个Controller中重复编写异常处理代码
差异化响应处理
自定义异常处理(DebounceException)
专门处理防抖逻辑相关的异常
使用异常自带的错误码和消息(
e.getCode()
和e.getMessage()
)示例场景:限制用户频繁提交表单/重复请求
全局兜底处理(Exception.class)
捕获所有未被特殊处理的异常
关键安全实践:禁止使用
printStackTrace()
(会暴露敏感信息)正确做法:通过日志框架记录完整堆栈(
log.error("msg", e)
)
空消息安全处理
使用
StringUtils.hasLength()
检查消息有效性提供默认友好提示("操作失败"/"下载文件失败")
避免返回空错误信息导致客户端解析失败
HTTP状态码管理
普通请求:返回500错误码(通过Result对象封装)
文件请求:设置HTTP状态为500(
HttpStatus.INTERNAL_SERVER_ERROR
)
最佳实践亮点:
日志规范
java
// 错误做法(禁止): 暴露堆栈到控制台 // e.printStackTrace(); // 正确做法(采用): 日志系统记录完整错误 log.error("异常详细信息: {}", e.getMessage(), e);
扩展性设计
通过
ErrorFileResponseUtils
工具类分离文件处理逻辑便于后续扩展其他文件类型处理
防御式编程
对
e.getMessage()
进行空值检测使用工具类方法判断请求类型(
isFileDownloadRequest
)
响应标准化
统一使用
Result
对象返回JSON格式错误
json
// 响应示例 { "code": 500, "msg": "操作失败", "data": null }
该实现有效解决了Spring Boot应用中的三个核心异常处理问题:
① 异常类型差异化处理 ② 响应形式自适配(JSON/文件) ③ 生产环境安全的错误日志记录