当前位置: 首页 > ai >正文

Spring Boot 全局异常处理

✅ 引言

在开发 Spring Boot 项目时,你是否遇到过这些问题?

  • ❌ 不同 Controller 重复写 try-catch
  • ❌ 异常信息格式不统一,前端难以解析
  • ❌ 系统内部错误直接暴露给用户(如堆栈信息)
  • ❌ 404、500 等状态码处理混乱

这些问题不仅影响用户体验,还可能带来安全风险。

全局异常处理就是 Spring Boot 为我们提供的“统一调度中心”,它能在异常发生时自动拦截、处理,并返回友好的响应。

本文将带你从零开始,构建一套生产级的全局异常处理机制,并结合实际代码,让你彻底掌握这一核心技能。


📌 一、为什么需要全局异常处理?

想象一个电商系统:

  • 用户下单时库存不足 → 返回“库存不足,请稍后再试”
  • 订单ID格式错误 → 返回“订单不存在”
  • 数据库连接失败 → 记录日志,返回“系统繁忙,请稍后重试”

如果没有统一处理,每个接口都要写类似的 try-catch,代码重复且难以维护。

而有了全局异常处理,就像设立了一个“客户服务中心”,所有异常都由它统一接待、分类处理、礼貌回应。


📌 二、核心技术:@ControllerAdvice@ExceptionHandler

Spring Boot 提供了两个核心注解来实现全局异常处理:

注解作用
@ControllerAdvice定义全局异常处理器(可作用于所有 Controller)
@ExceptionHandler指定处理某类异常的方法

📌 三、实战:构建统一异常处理机制

3.1 定义统一返回格式

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {private int code;private String message;private T data;// 成功响应public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(200, "success", data);}// 失败响应public static <T> ApiResponse<T> error(int code, String message) {return new ApiResponse<>(code, message, null);}
}

3.2 自定义业务异常

// 业务异常基类
public class BusinessException extends RuntimeException {private int code;public BusinessException(int code, String message) {super(message);this.code = code;}public BusinessException(String message) {super(message);this.code = 500;}// getterpublic int getCode() {return code;}
}

使用示例:

@Service
public class OrderService {public void createOrder(Long productId, Integer quantity) {if (quantity <= 0) {throw new BusinessException(400, "购买数量必须大于0");}// ... 其他逻辑}
}

3.3 全局异常处理器(核心)

@RestControllerAdvice  // 等价于 @ControllerAdvice + @ResponseBody
@Slf4j
public class GlobalExceptionHandler {/*** 处理自定义业务异常*/@ExceptionHandler(BusinessException.class)public ApiResponse<String> handleBusinessException(BusinessException e) {log.warn("业务异常: {}", e.getMessage());return ApiResponse.error(e.getCode(), e.getMessage());}/*** 处理参数校验异常(@Valid)*/@ExceptionHandler(MethodArgumentNotValidException.class)public ApiResponse<String> handleValidationException(MethodArgumentNotValidException e) {// 获取第一个错误信息String errorMessage = e.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).findFirst().orElse("参数校验失败");log.warn("参数校验异常: {}", errorMessage);return ApiResponse.error(400, errorMessage);}/*** 处理空指针异常*/@ExceptionHandler(NullPointerException.class)public ApiResponse<String> handleNullPointerException(NullPointerException e) {log.error("空指针异常", e);return ApiResponse.error(500, "系统内部错误,请联系管理员");}/*** 处理所有未被捕获的异常(兜底)*/@ExceptionHandler(Exception.class)public ApiResponse<String> handleException(Exception e) {log.error("未处理异常", e);return ApiResponse.error(500, "系统繁忙,请稍后重试");}/*** 处理 404 Not Found*/@ExceptionHandler(NoHandlerFoundException.class)@ResponseStatus(HttpStatus.NOT_FOUND)public ApiResponse<String> handle404(NoHandlerFoundException e) {log.warn("请求路径不存在: {}", e.getRequestURL());return ApiResponse.error(404, "请求的资源不存在");}
}

3.4 启用 404 异常捕获(重要!)

默认情况下,404 异常不会进入 @ExceptionHandler,需在配置文件中开启:

# application.yml
spring:mvc:throw-exception-if-no-handler-found: true  # 找不到处理器时抛出异常web:resources:add-mappings: false  # 关闭默认静态资源映射(可选,更严格)

📌 四、测试验证

4.1 创建测试 Controller

@RestController
@RequestMapping("/api")
public class TestController {@GetMapping("/business")public ApiResponse<String> businessError() {throw new BusinessException(400, "用户名已存在");}@PostMapping("/validate")public ApiResponse<String> validate(@Valid @RequestBody UserForm form) {return ApiResponse.success("验证通过");}@GetMapping("/null")public ApiResponse<String> nullPointer() {String str = null;str.length(); // 触发 NullPointerExceptionreturn ApiResponse.success("success");}@GetMapping("/unknown")public ApiResponse<String> unknown() {throw new RuntimeException("未知错误");}
}
class UserForm {@NotBlank(message = "用户名不能为空")private String username;@Min(value = 18, message = "年龄不能小于18岁")private Integer age;// getter & setter
}

4.2 测试结果

请求响应
GET /api/business{"code":400,"message":"用户名已存在","data":null}
POST /api/validate(无参数){"code":400,"message":"用户名不能为空","data":null}
GET /api/null{"code":500,"message":"系统内部错误,请联系管理员","data":null}
GET /api/unknown{"code":500,"message":"系统繁忙,请稍后重试","data":null}
GET /api/not-exist{"code":404,"message":"请求的资源不存在","data":null}

📌 五、高级技巧与生产实践

5.1 异常分类管理

你可以为不同模块定义不同的异常处理器:

// 用户模块异常处理器
@ControllerAdvice("com.example.controller.user")
public class UserExceptionHandler { ... }// 订单模块异常处理器
@ControllerAdvice("com.example.controller.order")
public class OrderExceptionHandler { ... }

5.2 结合 AOP 记录异常日志

@Aspect
@Component
public class ExceptionLogAspect {@AfterThrowing(pointcut = "@within(org.springframework.web.bind.annotation.RestController)", throwing = "ex")public void logException(JoinPoint joinPoint, Exception ex) {String methodName = joinPoint.getSignature().getName();log.error("方法 {} 发生异常: {}", methodName, ex.getMessage());}
}

5.3 返回错误码枚举(推荐)

public enum ErrorCode {SUCCESS(200, "成功"),BAD_REQUEST(400, "请求参数错误"),UNAUTHORIZED(401, "未授权"),FORBIDDEN(403, "禁止访问"),NOT_FOUND(404, "资源不存在"),SERVER_ERROR(500, "系统内部错误");private final int code;private final String message;ErrorCode(int code, String message) {this.code = code;this.message = message;}// getter
}

使用:

throw new BusinessException(ErrorCode.BAD_REQUEST);

✅ 总结:全局异常处理的最佳实践

实践说明
✅ 使用 @RestControllerAdvice统一返回 JSON 格式
✅ 自定义 BusinessException区分业务异常与系统异常
✅ 记录日志@Slf4j + log.error/warn
✅ 敏感信息脱敏不要将数据库错误、堆栈信息暴露给前端
✅ 合理分类异常优先处理具体异常,最后是 Exception
✅ 启用 404 捕获配置 throw-exception-if-no-handler-found: true
http://www.xdnf.cn/news/18146.html

相关文章:

  • 重置iPhone会删除所有内容吗? 详细回答
  • 基于WebSocket和SpringBoot聊天项目ChatterBox测试报告
  • 如何用Prometheus和FastAPI打造任务监控的“火眼金睛”?
  • 11.Ansible自动化之-内容集管理
  • 如何理解关系型数据库的ACID?
  • 浅看架构理论(一)
  • 【上升跟庄买入】副图/选股指标,动态黄色线由下向上穿越绿色基准线时,发出买入信号
  • 机器学习深度学习 所需数据的清洗实战案例 (结构清晰、万字解析、完整代码)包括机器学习方法预测缺失值的实践
  • RWA在DeFi中的应用
  • 【计算机视觉】检测与分割详解
  • AMPAK正基科技系列产品有哪些广泛应用于IOT物联网
  • spring声明式事务,finally 中return对事务回滚的影响
  • Spring Cloud系列—Seata分布式事务解决方案AT模式
  • 大模型落地选择困难症?RAG、Workflow、Agent全解析
  • Python脚本每天爬取微博热搜-终版
  • 在线客服系统访客表的设计与实现-增加最新消息字段
  • C#项目集成海康SDK指南:从搭建环境到实现视频预览、录制、截屏
  • 【LeetCode】16. 最接近的三数之和
  • 图论——Bellman-Ford和SPFA
  • 大模型+RPA:如何用AI实现企业流程自动化的“降本增效”?
  • traceroute命令使用指南
  • Linux学习-5网络管理
  • 企业如何让内部视频仅限指定域名播放,确保视频不被泄露?
  • SpreadJS 协同服务器 MongoDB 数据库适配支持
  • Flink Checkpoint 原理深度剖析与作用讲解(flink面试高频问题)
  • RK3128增加usb调试模式,开放adb和root权限
  • 分布式搜索(Elasticsearch)深入用法
  • 基于Python的宠物服务管理系统 Python+Django+Vue.js
  • 卫生许可证识别技术:通过OCR与NLP实现高效合规管理,提升审核准确性与效率
  • 传输层协议——UDP和TCP