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

【实战案例】SpringBoot项目中异常处理通用解决方案

项目中经常会出现一些异常,比如在新增项目的时候必要的字段没有填写。在springboot项目中,遇到异常会往上抛出给调用方,DAO层遇到异常抛给Service层,Service层遇到异常抛给Controller层,Controller层遇到异常就抛给了SpringMVC框架。对于前端来说是无法感知的,并不知道具体发生了什么错误。如果使用try-catch的话代码又过于冗余,所以需要把异常处理的逻辑统一进行管理。
在这里插入图片描述
上图是设想的解决方案,通过SpringMVC提供的控制器增强类统一由一个类完成异常的捕获,背后是AOP机制。

实现过程:
枚举类CommonError用于定义常见的错误类型

package com.gavin.base.exception;

/**
 * @author Gavin
 * @description 通用错误信息
 * @date 2024/10/15
 **/
public enum CommonError {

    UNKOWN_ERROR("执行过程异常,请重试。"),
    PARAMS_ERROR("非法参数"),
    OBJECT_NULL("对象为空"),
    QUERY_NULL("查询结果为空"),
    REQUEST_NULL("请求参数为空");

    private String errMessage;

    public String getErrMessage() {
        return errMessage;
    }

    private CommonError( String errMessage) {
        this.errMessage = errMessage;
    }
}

自定义异常类EffecttiveStudyException用来封装项目中特定异常信息

package com.gavin.base.exception;

/**
 * @author Gavin
 * @description 自定义高效学习在线类项目异常类
 * @date 2024/10/15
 **/
public class EffectiveStudyException extends RuntimeException{
    private String errMessage;

    public EffectiveStudyException() {
        super();
    }

    public EffectiveStudyException(String errMessage) {
        super(errMessage);
        this.errMessage = errMessage;
    }

    public String getErrMessage() {
        return errMessage;
    }

    public static void cast(CommonError commonError){
        throw new EffectiveStudyException(commonError.getErrMessage());
    }
    public static void cast(String errMessage){
        throw new EffectiveStudyException(errMessage);
    }
}

错误响应类RestErrorResponse用于统一封装并返回给前端的错误信息

package com.gavin.base.exception;

import java.io.Serializable;

/**
 * @author Gavin
 * @description 和前端约定返回的异常信息
 * @date 2024/10/15
 **/
public class RestErrorResponse implements Serializable {

    private String errMessage;

    public RestErrorResponse(String errMessage){
        this.errMessage= errMessage;
    }

    public String getErrMessage() {
        return errMessage;
    }

    public void setErrMessage(String errMessage) {
        this.errMessage = errMessage;
    }
}

定义全局异常处理器GlobalExceptionHandler类,这个类可以认为就是AOP机制中的一个切面,在系统各种专门处理所有控制器方法抛出的异常。以下是两个比较重要的注解:
@ControllerAdvice:用来标识一个全局异常处理类,所有被@Controller注解的控制器中的异常都会被它捕获。
@ExceptionHandler:用于捕获特定类型的异常。在这里,EffectiveStudyException 和其他普通异常都分别有处理方法。

package com.gavin.base.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @author Gavin
 * @description 全局异常处理器
 * @date 2024/10/15
 **/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(EffectiveStudyException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestErrorResponse customException(EffectiveStudyException e) {
        log.error("【系统异常】{}",e.getErrMessage(),e);
        return new RestErrorResponse(e.getErrMessage());

    }

    @ResponseBody
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestErrorResponse exception(Exception e) {

        log.error("【系统异常】{}",e.getMessage(),e);

        return new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());
    }
}

通过上述全局异常处理器,任何抛出的EffectiveStudyException或未被预期的异常都会被这个类统一处理,并返回一个标准的错误信息。

上述自定义后,对于抛出异常的地方可以进行替换,例如

throw new RuntimeException("课程的价格不能为空且必须大于0");

可以替换为

throw new EffectiveStudyException("课程的价格不能为空且必须大于0");
或者
EffectiveStudyException.cast("课程的价格不能为空且必须大于0");

两种形式是等价的,但后者更为简洁。如果捕获了上述异常,前端返回的就是对应内容的json数据信息,如下:
在这里插入图片描述

当存在其他异常时,如controller中存在一个除数为0的情形,如果在Controller中没有进行显示的try-catch捕获,异常会被直接抛出,由SpringMVC框架捕获并向上传递,SpringMVC会将这个异常传递给全局异常处理器类(GlobalExceptionHandler类),由于不是EffectiveStudyException类型的错误,所以交由兜底Exception处理,返回了UNKOWN_ERROR对应的错误信息(这里对于常见错误可定义特定信息返回)。如下:
在这里插入图片描述

这里可能会存在疑问,为什么不直接返回e.getMessage()的具体信息而是笼统的执行过程异常请重试?考虑的因素有:异常具体信息中可能包含敏感的系统内部信息,如数据库查询语句,服务器路径及框架细节等信息,有风险。另外,复杂的信息用户体验不好,提示信息应该简洁友好。但实际中开发环境有时候是有必要放出具体异常信息的,生产环境不适合放具体异常信息。


http://www.kler.cn/news/354756.html

相关文章:

  • 单片机原理与应用——嵌入式系统中的核心控制器
  • MySQL从入门到跑路
  • 干货|antd组件库Table组件开启虚拟列表的影响
  • 深度解析RLS(Recursive Least Squares)算法
  • 【Spring篇】初识之Spring的入门程序及控制反转与依赖注入
  • 如何利用被动DNS(Passive DNS)加强网络安全
  • STM32学习笔记---RTC
  • 中级注册安全工程师《安全生产法律法规》真题及详解
  • 48 | 代理模式:代理在RPC、缓存、监控等场景中的应用
  • 分布式管理工具分析:Java、Go 和 Python
  • 【vue】keep-alive动态组件的基础用法
  • 【text2sql】基于上下文文学习的MCS-SQL框架在Spider和BIRD取得了新SOTA
  • 线性可分支持向量机的原理推导
  • Android Jetpack组件库中的LiveData和ViewModel的作用。
  • 探索OpenCV的人脸检测:用Haar特征分类器识别图片中的人脸
  • [含文档+PPT+源码等]精品基于springboot实现的原生微信小程序汽车保养服务
  • 绿幕虚拟直播五大“硬件环境”
  • D2000国产化加固笔记本电脑:筑牢信息安全防线
  • Java学习-JUC
  • Java封装,继承,多态