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

Spring Boot 全局异常处理

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

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

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

Spring Boot 中的全局异常处理:

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

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

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

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;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

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

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 中抛出自定义的异常,测试全局异常处理是否生效。

@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.kler.cn/a/507736.html

相关文章:

  • 合格的前端,使用xlsx
  • tomcat文件目录讲解
  • 【网络 MAC 学习专栏 -- 如何理解 PHY 的 Link Up】
  • (01)FreeRTOS移植到STM32
  • 数据结构--二叉树
  • 机器学习06-正则化
  • 第8篇:从入门到精通:掌握Python异常处理
  • Redis系列之底层数据结构整数集IntSet
  • .Net Core webapi 实现JWT认证
  • 知识图谱综述论文阅读(一)
  • AI大模型架构背后的数学原理和数学公式,基于Transformer架构的数学公式有哪些?
  • 寒假刷题Day8
  • 【影刀_常规任务计划_API调用】
  • 深度学习-87-大模型训练之预训练和微调所用的数据样式
  • 基于PHP的校园新闻发布管理
  • Go入门学习笔记
  • SQL ON与WHERE区别
  • 架构设计:微服务还是集群更适合?
  • Java负载均衡
  • C++ 强化记忆
  • 【Linux系统】分区挂载
  • 图像的旋转之c++实现(qt + 不调包)_c++图像旋转
  • 晨辉面试抽签和评分管理系统之十:如何搭建自己的数据库服务器,使用本软件的网络版
  • 【机器学习实战入门】有趣的Python项目:使用OpenCV进行性别和年龄检测
  • [Mac + Icarus Verilog + gtkwave] Mac运行Verilog及查看波形图
  • 计算机网络 (47)应用进程跨越网络的通信