Spring Boot 全局异常处理

embedded/2025/1/18 21:04:09/

  在开发任何应用程序时,异常处理都是至关重要的。一个良好的异常处理机制不仅能提高用户体验,还能帮助开发者更好地定位和修复问题。Spring Boot 提供了强大的异常处理能力,使我们能够集中处理应用程序中抛出的各种异常,从而构建更健壮和可靠的系统。
  本文将深入探讨 Spring Boot 中全局异常处理的最佳实践,包括如何创建全局异常处理器,如何自定义异常响应,以及如何处理不同类型的异常。

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

  1. 避免用户看到丑陋的错误页面:未处理的异常会导致用户看到默认的错误页面,这不仅不友好,还可能暴露敏感信息。
  2. 统一异常响应格式:通过全局异常处理,可以统一应用程序的异常响应格式,方便客户端解析和处理错误。
  3. 简化代码逻辑:将异常处理逻辑集中到一个地方,可以减少在各个业务逻辑代码中重复编写异常处理代码。
  4. 提高系统健壮性:全局异常处理可以捕获应用程序中未知的异常,避免程序崩溃。
  5. 方便日志记录和监控:将异常集中处理,方便记录日志和进行监控,帮助开发者更好地了解系统运行情况。

Spring Boot 中的全局异常处理:

  Spring Boot 提供了两个注解来实现全局异常处理:@ControllerAdvice@ExceptionHandler
@ControllerAdvice: 这个注解声明一个类为全局异常处理类,该类可以捕获所有 @Controller 中抛出的异常。
@RestControllerAdvice:作用于所有使用了 @RestController 或者 @Controller + 方法级别 @ResponseBody 注解的类。
@ExceptionHandler: 这个注解用于指定一个方法来处理特定的异常。

实践:创建全局异常处理器

1、定义自定义异常类: 首先,我们可以定义一些自定义异常类,用于表示业务上的异常,比如用户不存在,参数错误等

java">public class ServiceException extends RuntimeException{/*** 错误码*/private Integer code;/*** 错误提示*/private String message;public ServiceException(String message){this.message = message;}public ServiceException(Integer code, String message) {this.message = message;this.code = code;}public ServiceException(Status status, Object... statusParams){this.code = status.getCode();if(statusParams != null && statusParams.length > 0){this.message = MessageFormat.format(status.getMsg(), statusParams);} else {this.message = status.getMsg();}}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}@Overridepublic String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}

2、创建全局异常处理类: 创建一个类,并使用 @ControllerAdvice 注解声明该类为全局异常处理类

java">import cn.hutool.core.util.ObjectUtil;
import com.extend.chk.domain.Result;
import com.extend.chk.domain.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import javax.servlet.http.HttpServletRequest;@RestControllerAdvice
public class GlobalExceptionHandler {private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 拦截业务异常*/@ExceptionHandler(ServiceException.class)public Result handleServiceException(ServiceException e, HttpServletRequest request){logger.error("请求地址:{}, 发生业务异常: {}", request.getRequestURI(), e.getMessage(), e);Integer code = e.getCode();String msg = e.getMessage();return ObjectUtil.isNotNull(code) ? Result.error(code, msg) : Result.error(msg);}/*** 拦截未知的运行时异常*/@ExceptionHandler(RuntimeException.class)public Result handleRuntimeException(RuntimeException e, HttpServletRequest request) {String requestURI = request.getRequestURI();logger.error("请求地址'{}',发生未知异常.", requestURI, e);return Result.error(e.getMessage());}/*** 拦截系统异常*/@ExceptionHandler(Exception.class)public Result handleException(Exception e, HttpServletRequest request) {String requestURI = request.getRequestURI();logger.error("请求地址'{}',发生系统异常.", requestURI, e);return Result.error(e.getMessage());}}
  1. @ExceptionHandler(ServiceException.class):指定handleServiceException方法处理 ServiceException类型的异常。
  2. @ExceptionHandler(RuntimeException.class):指定handleRuntimeException方法处理 RuntimeException类型的异常。
  3. @ExceptionHandler(Exception.class):指定 handleException方法处理所有未被特定处理的异常, 并记录日志。

3、Controller 中抛出异常: 在 Controller 中抛出自定义的异常,测试全局异常处理是否生效。

java">@RestController
@RequestMapping("/yes")
public class ExtendController extends BaseController {@GetMapping("/users/{id}")public Integer getUser(@PathVariable int id) {if (id == 1) {throw new ServiceException("业务异常");} else if (id == 2) {throw new RuntimeException("非检查型异常");} else {int a = 1 / 0;return a;}}
}
  1. 当 id 为 1 时,抛出 ServiceException异常。 {“code”: 10000,“msg”: “业务异常”,“data”: null}
  2. 当 id 为 2 时,抛出 RuntimeException异常。 {“code”: 10000,“msg”: “非检查型异常”,“data”: null}
  3. 当 id 为其他值时,抛出 Exception 异常。 {“code”: 10000,“msg”: “/ by zero”,“data”: null}

最佳实践

  1. 针对不同异常类型制定不同的处理策略: 不同的异常和前端约定不同的返回方式。
  2. 不要暴露敏感信息:在异常响应中,不要暴露系统内部的敏感信息,比如数据库连接字符串等。
  3. 记录日志: 将异常信息记录到日志中,方便问题追踪。
  4. 统一响应格式:保持一致的响应格式,方便客户端处理。
  5. 提供友好的错误信息: 提供给用户的错误信息应该易于理解,而不是技术术语。

http://www.ppmy.cn/embedded/155043.html

相关文章

css‘s hover VS mobile

.animation {animation: 30s move infinite linear;/* &:hover {animation-play-state: paused;*/ }原本写的好好的,测试说:“移动端点击滚动条,跳转到其他页面后,返回当前页面,滚动条不滚动;可以优化位…

ubuntu 系统 ,docker建的服务 ,其他局网机器可以通过IP:端口的方式访问。不是docker的不行。

根据您的描述,docker 建的服务可以通过 IP:端口的方式被局网其他机器访问,而非 docker 的服务不行,以下是可能的原因及解决方法: 网络配置方面 • 检查非 docker 服务的网络监听配置:确保非 docker 服务是…

解决postman打开一直转圈圈的问题

Postman 打开时一直转圈圈的问题可能是由多种原因引起的。以下是一些常见的解决方法,你可以逐一尝试: 1. 重启 Postman 有时,简单的重启可以解决许多问题。 关闭 Postman。重新打开 Postman。 2. 清除缓存 Postman 有时会因为缓存问题导…

2021年前端部署的灵魂拷问

注意,缓存生效期间,浏览器是【自言自语】,和服务器无关。 此时,设置强缓存后,Network 大致变成了这样: image.png From DiskCache:从硬盘中读取。 From MemoryCache:从内存中读取&am…

Redis Cluster 集群

1. Redis Cluster 简介 Redis Cluster 是 Redis 官方提供的 Redis 集群功能。 为什么要实现 Redis Cluster? Redis 是单线程的(从网络 I/O 处理到实际的读写命令处理),无论单核 CPU 下内存多大,如果需要大量计算能力…

25/1/17 嵌入式笔记 STM32F103

#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h"uint8_t RxData; //定义用于接收串口数据的变量int main(void) {/*模块初始化*/OLED_Init(); //OLED初始化/…

ubuntu20.04 docker安装

Ubuntu | Docker DocsPost-installation steps | Docker Docs # 创建目录 sudo mkdir -p /etc/docker # 写入配置文件 sudo tee /etc/docker/daemon.json <<-EOF { "registry-mirrors": [ "https://docker-0.unsee.tech", &qu…

下载文件,浏览器阻止不安全下载

背景&#xff1a; 在项目开发中&#xff0c;遇到需要下载文件的情况&#xff0c;文件类型可能是图片、excell表、pdf、zip等文件类型&#xff0c;但浏览器会阻止不安全的下载链接。 效果展示&#xff1a; 下载文件的两种方式&#xff1a; 一、根据接口的相对url&#xff0c;拼…