• Spring Boot3自定义异常及全局异常捕获


     

    ⛰️个人主页:     蒾酒

    🔥系列专栏:《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应用程序开发中,会遇到各种异常有可预知的也有不可预知的,我们很少会每个过程做单独异常处理,通常会将各种类型的异常处理过程解耦出来,保证业务逻辑单一、相关异常处理单一

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

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

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

    主要步骤

    定义自定义异常类

    下面我们定义几个自定义异常

    1.账号不存在异常

    1. import com.mijiu.commom.enumerate.ResultEnum;
    2. import lombok.Getter;
    3. /**
    4. * 账户不存在异常
    5. *
    6. * @author mijiupro
    7. */
    8. @Getter
    9. public class AccountNotFoundException extends RuntimeException {
    10. private final ResultEnum resultEnum;
    11. public AccountNotFoundException(ResultEnum resultEnum) {
    12. this.resultEnum = resultEnum;
    13. }
    14. }

    2.密码错误异常

    1. import com.mijiu.commom.enumerate.ResultEnum;
    2. import lombok.Getter;
    3. /**
    4. * 密码错误异常
    5. *
    6. * @author mijiupro
    7. */
    8. @Getter
    9. public class PasswordErrorException extends RuntimeException {
    10. private final ResultEnum resultEnum;
    11. public PasswordErrorException(ResultEnum resultEnum) {
    12. this.resultEnum = resultEnum;
    13. }
    14. }

    3. 令牌过期异常

    1. import com.mijiu.commom.enumerate.ResultEnum;
    2. import lombok.Getter;
    3. /**
    4. * 令牌过期异常
    5. *
    6. * @author mijiupro
    7. */
    8. @Getter
    9. public class TokenOverdueException extends RuntimeException {
    10. private final ResultEnum resultEnum;
    11. public TokenOverdueException(ResultEnum resultEnum) {
    12. this.resultEnum = resultEnum;
    13. }
    14. }

    对于各种异常的错误码以及提示内容通常使用枚举类如下:

    1. import lombok.Getter;
    2. /**
    3. * @author mijiupro
    4. */
    5. @Getter
    6. public enum ResultEnum {
    7. /* 成功状态码 */
    8. SUCCESS(1, "操作成功!"),
    9. /* 错误状态码 */
    10. FAIL(0, "操作失败!"),
    11. /* 参数错误:10001-19999 */
    12. PARAM_IS_INVALID(10001, "参数无效"),
    13. PARAM_IS_BLANK(10002, "参数为空"),
    14. PARAM_TYPE_BIND_ERROR(10003, "参数格式错误"),
    15. PARAM_NOT_COMPLETE(10004, "参数缺失"),
    16. /* 用户错误:20001-29999*/
    17. USER_NOT_LOGGED_IN(20001, "用户未登录,请先登录"),
    18. USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
    19. USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"),
    20. USER_NOT_EXIST(20004, "用户不存在"),
    21. USER_HAS_EXISTED(20005, "用户已存在"),
    22. /* 系统错误:40001-49999 */
    23. FILE_MAX_SIZE_OVERFLOW(40003, "上传尺寸过大"),
    24. FILE_ACCEPT_NOT_SUPPORT(40004, "上传文件格式不支持"),
    25. /* 数据错误:50001-599999 */
    26. RESULT_DATA_NONE(50001, "数据未找到"),
    27. DATA_IS_WRONG(50002, "数据有误"),
    28. DATA_ALREADY_EXISTED(50003, "数据已存在"),
    29. AUTH_CODE_ERROR(50004, "验证码错误"),
    30. /* 权限错误:70001-79999 */
    31. PERMISSION_UNAUTHENTICATED(70001, "此操作需要登陆系统!"),
    32. PERMISSION_UNAUTHORIZED(70002, "权限不足,无权操作!"),
    33. PERMISSION_EXPIRE(70003, "登录状态过期!"),
    34. PERMISSION_TOKEN_EXPIRED(70004, "token已过期"),
    35. PERMISSION_LIMIT(70005, "访问次数受限制"),
    36. PERMISSION_TOKEN_INVALID(70006, "无效token"),
    37. PERMISSION_SIGNATURE_ERROR(70007, "签名失败");
    38. // 状态码
    39. int code;
    40. // 提示信息
    41. String message;
    42. ResultEnum(int code, String message) {
    43. this.code = code;
    44. this.message = message;
    45. }
    46. public int code() {
    47. return code;
    48. }
    49. public String message() {
    50. return message;
    51. }
    52. public void setCode(int code) {
    53. this.code = code;
    54. }
    55. public void setMessage(String message) {
    56. this.message = message;
    57. }
    58. }

    创建全局异常处理器

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

    1. import com.mijiu.commom.exception.AccountNotFoundException;
    2. import com.mijiu.commom.exception.PasswordErrorException;
    3. import com.mijiu.commom.exception.TokenOverdueException;
    4. import com.mijiu.commom.result.Result;
    5. import com.mijiu.commom.enumerate.ResultEnum;
    6. import lombok.extern.slf4j.Slf4j;
    7. import org.springframework.http.HttpStatus;
    8. import org.springframework.web.bind.annotation.*;
    9. /**
    10. * @author mijiupro
    11. */
    12. @RestControllerAdvice(basePackages = "com.mijiu.controller")
    13. @ResponseBody
    14. @Slf4j
    15. public class GlobalExceptionHandler {
    16. // 账号不存在异常
    17. @ExceptionHandler(AccountNotFoundException.class)
    18. @ResponseStatus(HttpStatus.BAD_REQUEST)
    19. public Result handleAccountNotFoundException(AccountNotFoundException ex) {
    20. return Result.error(ex.getResultEnum());
    21. }
    22. // 密码错误异常
    23. @ExceptionHandler(PasswordErrorException.class)
    24. @ResponseStatus(HttpStatus.BAD_REQUEST)
    25. public Result handlePasswordErrorException(PasswordErrorException ex) {
    26. return Result.error(ex.getResultEnum());
    27. }
    28. // 登录状态过期异常
    29. @ExceptionHandler(TokenOverdueException.class)
    30. @ResponseStatus(HttpStatus.UNAUTHORIZED)
    31. public Result handleTokenOverdueException(TokenOverdueException ex){
    32. return Result.error(ex.getResultEnum());
    33. }
    34. /**
    35. *
    36. * 通用异常处理
    37. */
    38. @ExceptionHandler(Exception.class)
    39. public Result exceptionHandler(Exception ex) {
    40. log.error(ex.getMessage());
    41. return Result.error(ResultEnum.FAIL);
    42. }
    43. }

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

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

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

    1. import com.mijiu.commom.enumerate.ResultEnum;
    2. import lombok.Data;
    3. /**
    4. * @author mijiupro
    5. */
    6. @Data
    7. public class Result {
    8. // 操作代码
    9. Integer code;
    10. // 提示信息
    11. String message;
    12. // 结果数据
    13. T data;
    14. public Result(ResultEnum resultCode) {
    15. this.code = resultCode.code();
    16. this.message = resultCode.message();
    17. }
    18. public Result(ResultEnum resultCode, T data) {
    19. this.code = resultCode.code();
    20. this.message = resultCode.message();
    21. this.data = data;
    22. }
    23. public Result(String message) {
    24. this.message = message;
    25. }
    26. //成功返回封装-无数据
    27. public static Result success() {
    28. return new Result(ResultEnum.SUCCESS);
    29. }
    30. //成功返回封装-带数据
    31. public static Result success(T data) {
    32. return new Result(ResultEnum.SUCCESS, data);
    33. }
    34. //失败返回封装-使用默认提示信息
    35. public static Result error() {
    36. return new Result(ResultEnum.FAIL);
    37. }
    38. //失败返回封装-使用返回结果枚举提示信息
    39. public static Result error(ResultEnum resultCode) {
    40. return new Result(resultCode);
    41. }
    42. //失败返回封装-使用自定义提示信息
    43. public static Result error(String message) {
    44. return new Result(message);
    45. }
    46. }

    手动抛出自定义异常

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

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

    像这样直接抛出即可

  • 相关阅读:
    分类接口,淘宝分类详情 API
    架构师必修设计模式——结构型模式
    CAD快捷键——修改类
    信号量的使用
    java毕业设计——基于java+Eclipse的扫雷游戏设计与实现(毕业论文+程序源码)——扫雷游戏
    MVC、MVP、MVVM三种架构对比
    网页制作课作业基于HTML+CSS+JavaScript+jquery仿慕课网教学培训网站设计实例 企业网站制作
    Makefile(二)
    ls命令区别
    Golang开发--channel的使用
  • 原文地址:https://blog.csdn.net/qq_62262918/article/details/136110267