⛰️个人主页:     蒾酒

🔥系列专栏:《spring boot实战》


目录

前置条件

目的

主要步骤

定义自定义异常类

创建全局异常处理器

手动抛出自定义异常


前置条件

已经初始化好一个spring boot项目且版本为3X,项目可正常启动。

作者版本为3.2.2

初始化教程:

新版idea(2023)创建spring boot3项目-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/135785412?spm=1001.2014.3001.5501

目的

Spring Boot应用程序开发中,会遇到各种异常有可预知的也有不可预知的,我们很少会每个过程做单独异常处理,通常会将各种类型的异常处理过程解耦出来,保证业务逻辑单一、相关异常处理单一

通常将异常进行处理,封装一下对应错误信息返回友好信息。避免把异常直接给前端、用户。

反例:异常直接返回(不友好)

正例:处理后返回提示信息(友好)

主要步骤

自定义异常类

下面我们举例定义几种常用异常类:

第一种:通用业务异常类

有一些业务异常可能仅仅是返回的错误提示信息的不同,并不需要额外的特殊逻辑处理,我们仅仅在业务代码里面将它抛出来触发对应返回处理。

如图:

代码如下:

import lombok.Getter;
import lombok.Setter;

/**
 * @author mijiupro
 * 通用业务异常类
 */
@Getter
@Setter
public class GeneralBusinessException extends RuntimeException{
    private int code=0;
    private String message;

    public GeneralBusinessException(String message) {
        this.message = message;
    }

}

第二种:特殊处理业务异常类

可能处于安全性考虑,通常会进行密码错误次数限制,每次出现密码错误异常就需要进行累加

对于这种需要进行特殊处理的业务异常,我们就需要单独定义出对应异常类。

代码如下:

import com.mijiu.commom.enumerate.ResultEnum;
import lombok.Getter;

/**
 * 密码错误异常
 *
 * @author mijiupro
 */
@Getter
public class PasswordErrorException extends RuntimeException {
    private final ResultEnum resultEnum;
    
    public PasswordErrorException(ResultEnum resultEnum) {
        this.resultEnum = resultEnum;
    }

}

对于各种业务异常的对应错误提示信息通常可以通过一个枚举类来维护。

import lombok.Getter;

/**
 * @author mijiupro
 */
@Getter
public enum ResultEnum {

    /* 成功状态码 */
    SUCCESS(1, "操作成功!"),

    /* 错误状态码 */
    FAIL(0, "操作失败!"),

    /* 参数错误:10001-19999 */
    PARAM_IS_INVALID(10001, "参数无效"),
    PARAM_IS_BLANK(10002, "参数为空"),
    PARAM_TYPE_BIND_ERROR(10003, "参数格式错误"),
    PARAM_NOT_COMPLETE(10004, "参数缺失"),

    /* 用户错误:20001-29999*/
    USER_NOT_LOGGED_IN(20001, "用户未登录,请先登录"),
    USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
    USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"),
    USER_NOT_EXIST(20004, "用户不存在"),
    USER_HAS_EXISTED(20005, "用户已存在"),

    /* 系统错误:40001-49999 */
    FILE_MAX_SIZE_OVERFLOW(40003, "上传尺寸过大"),
    FILE_ACCEPT_NOT_SUPPORT(40004, "上传文件格式不支持"),

    /* 数据错误:50001-599999 */
    RESULT_DATA_NONE(50001, "数据未找到"),
    DATA_IS_WRONG(50002, "数据有误"),
    DATA_ALREADY_EXISTED(50003, "数据已存在"),
    AUTH_CODE_ERROR(50004, "验证码错误"),


    /* 权限错误:70001-79999 */
    PERMISSION_UNAUTHENTICATED(70001, "此操作需要登陆系统!"),

    PERMISSION_UNAUTHORIZED(70002, "权限不足,无权操作!"),

    PERMISSION_EXPIRE(70003, "登录状态过期!"),

    PERMISSION_TOKEN_EXPIRED(70004, "token已过期"),

    PERMISSION_LIMIT(70005, "访问次数受限制"),

    PERMISSION_TOKEN_INVALID(70006, "无效token"),

    PERMISSION_SIGNATURE_ERROR(70007, "签名失败");

    // 状态码
    int code;
    // 提示信息
    String message;

    ResultEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int code() {
        return code;
    }

    public String message() {
        return message;
    }

    public void setCode(int code) {
        this.code = code;
    }

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

创建全局异常处理器

定义的各种业务异常对应处理逻辑代码通常在全局异常处理器来写。

需要创建一个全局异常处理器类,用于捕获和处理所有的异常。可以使用@ControllerAdvice  或者@RestControllerAdvice注解将该类标记为全局异常处理器,并使用@ExceptionHandler 注解定义具体的异常处理方法。例如,创建一个名为 GlobalExceptionHandler 的全局异常处理器类:

import com.mijiu.commom.exception.AccountNotFoundException;
import com.mijiu.commom.exception.PasswordErrorException;
import com.mijiu.commom.exception.TokenOverdueException;
import com.mijiu.commom.result.Result;
import com.mijiu.commom.enumerate.ResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

/**
 * @author mijiupro
 */
@RestControllerAdvice(basePackages = "com.mijiu.controller")
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {



    // 密码错误异常
    @ExceptionHandler(PasswordErrorException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<String> handlePasswordErrorException(PasswordErrorException ex) {
        //如通过redis进行密码错误计数器累加操作
        //。。。。。。。。。。。。。
        
        //日志记录等。。。。。。。。。

        return Result.error(ex.getResultEnum());
    }

 

    //通用业务异常处理
    @ExceptionHandler(GeneralBusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<String> generalBusinessExceptionHandler(GeneralBusinessException ex) {
        return Result.error(ex.getMessage());
    }

    /**
     *
     * 通用异常处理(用于处理不可预知的异常)
     */
    @ExceptionHandler(Exception.class)
    public Result<String> exceptionHandler(Exception ex) {
        log.error(ex.getMessage());
        return Result.error(ResultEnum.FAIL);
    }
}

对于不可预知的异常通常直接捕获所有异常父类Exception异常即可

这段代码会触发ArithmeticException ,类似这种的不可预知异常会有很多,它们都继承自Exception所以全局拦截Exception进行处理即可。

对于异常的处理返回的信息通常用统一格式封装:

import com.mijiu.commom.enumerate.ResultEnum;
import lombok.Data;


/**
 * @author mijiupro
 */

@Data
public class Result<T> {

    // 操作代码
    Integer code;

    // 提示信息
    String message;

    // 结果数据
    T data;

    public Result(ResultEnum resultCode) {
        this.code = resultCode.code();
        this.message = resultCode.message();
    }

    public Result(ResultEnum resultCode, T data) {
        this.code = resultCode.code();
        this.message = resultCode.message();
        this.data = data;
    }
    public Result(String message) {
        this.message = message;
    }
    //成功返回封装-无数据
    public static Result<String> success() {
        return new Result<String>(ResultEnum.SUCCESS);
    }
    //成功返回封装-带数据
    public static <T> Result<T> success(T data) {
        return new Result<T>(ResultEnum.SUCCESS, data);
    }
    //失败返回封装-使用默认提示信息
    public static Result<String> error() {
        return new Result<String>(ResultEnum.FAIL);
    }
    //失败返回封装-使用返回结果枚举提示信息
    public static Result<String> error(ResultEnum resultCode) {
        return new Result<String>(resultCode);
    }
    //失败返回封装-使用自定义提示信息
    public static Result<String> error(String message) {
        return new Result<String>(message);

    }
}

手动抛出自定义异常

在业务代码中抛出对应业务自定义异常:

有参构造传递错误枚举信息(状态码+错误内容),全局异常捕获并从自定义异常类拿到相关返回信息统一封装返回。

像这样直接抛出即可

更多推荐