【SpringBoot】统一异常处理

业务需求背景:

需求说明:为了不在controller编写大量的try-catch代码,需要进行统一异常处理,同时要进行错误信息以及错误码的统一管理,建议使用枚举进行错误码封装同时要求系统支持JSR303校验规则。

统一异常处理的思路:

  1. 创建一个全局的异常处理器(Global Exception Handler):定义一个全局的异常处理类(异常处理器),用于捕获和处理所有未被捕获的异常。
  2. 定义异常处理方法:在全局异常处理器中定义异常处理方法,用于处理不同类型的异常。可以根据异常的类型、错误代码、错误信息等来进行分类处理,这里用的是通过异常类型来分类@ExceptionHandler注解标识这个方法处理哪个类型的异常:
  3. 注册全局异常处理器:将全局异常处理器注册到应用程序中,使其成为默认的异常处理机制。具体的注册方式取决于应用程序的架构和框架,可以是通过配置文件、注解或代码等方式进行注册,这个地方是用的注解的方式:@RestControllerAdvice复合注解中有一个**@ControllerAdvice注解**将控制所有的Controller层并拦截所有的异常
  4. 异常处理逻辑:这里处理的逻辑是将错误码信息封装到枚举类中将对应的异常的枚举类中的错误码信息给到自定义的异常中再封装到AjaxResult响应结果中,响应到客户端

代码:

异常类:

两个成员:
code: 错误码
globalMessage: 异常信息
注意:
使用枚举可以快速的进行查找和设计了

package com.noting.basic.exception;

import lombok.Data;

/**
 * 自定义全局异常类GlobalException
 */
@Data
public class GlobalException extends RuntimeException{

    private String code;
    private String globalMessage;

    public GlobalException() {
    }

    public GlobalException(String message) {
        super(message);
    }

    public GlobalException(String code, String message) {
        super(message);
        this.code = code;
        this.globalMessage = message;
    }

    // 建立直接传入枚举做参数可以直接创建对应的异常
    public GlobalException(GlobalExceptionEnum globalExceptionEnum) {
        super(globalExceptionEnum.getMessage());
        this.code = globalExceptionEnum.getCode();
        this.globalMessage = globalExceptionEnum.getMessage();
    }
}

枚举类封装错误码信息:

package com.noting.basic.exception;

import lombok.Getter;


/**
 * 枚举类封装错误码信息:
 */
@Getter
public enum GlobalExceptionEnum {
    // 1. 字段(枚举实例)
    ERROR("-1", "系统异常,请稍后再试!"),

    SUCCESS("0", "操作成功!"),

    PHONE_IS_NULL_ERROR ("1001", "电话不能为空"),

    PARAM_ERROR ("1002", "参数校验异常"),

    DELETE_ERROR ("1101", "删除错误"),

    UPLOAD_ERROR("1102", "上传文件失败错误"),
    ;


    // 2. 枚举实例
    private String code; // 错误码
    private String message; // 错误码对应的错误信息提示语

    // 3. 字段的构造方法


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

全局异常的处理类:

image.png

  • 全局异常的处理类**@RestControllerAdvice**
    *** **@RestControllerAdvice 该注解是一个复合注解包括下面两个注解
    • **@ControllerAdvice ** 将控制所有的Controller层并拦截所有的异常
    • @ResponseBody 将本类中所有方法的返回值转换为JSON
      相当于每次抛出异常就进入这个地方操作
      注意点:
      异常的类型不同, 需求不同可以继续添加
package com.noting.basic.exception;

import com.noting.basic.utils.AjaxResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常的处理器@RestControllerAdvice
 *  @RestControllerAdvice 该注解是一个复合注解包括下面两个注解
    *  @ControllerAdvice  将控制所有的Controller层并拦截所有的异常
    *  @ResponseBody       将本类中所有方法的返回值转换为JSON
 */
@RestControllerAdvice // 必须必须要被启动类扫描
public class GlobalExceptionHandler {

    /**
     * 处理系统异常
     * @return
     *
     * catch (Exception e) {
     *             e.printStackTrace();
     *             return AjaxResult.me().setSuccess(false).setMessage("系统繁忙,请重试!");
     *         }
     */
    @ExceptionHandler(Exception.class) //这句代码可以认为是trycatch中的catch
    public AjaxResult exceptionHandler(Exception e){
        e.printStackTrace();
        return AjaxResult.error(GlobalExceptionEnum.ERROR.getCode(), GlobalExceptionEnum.ERROR.getMessage());
    }

    /**
     * 处理自定义业务异常
     * @return
     *
     * catch (GlobalException e) {
     *             e.printStackTrace();
     *             return AjaxResult.me().setSuccess(false).setMessage("系统繁忙,请重试!");
     *         }
     */
    @ExceptionHandler(GlobalException.class) //这句代码可以认为是trycatch中的catch
    public AjaxResult globalExceptionHandler(GlobalException e){
        e.printStackTrace();
        return AjaxResult.error(e.getCode(), e.getGlobalMessage());
    }


    /**
     * 处理校验对象时候的异常MethodArgumentNotValidException
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public AjaxResult globalExceptionHandler(MethodArgumentNotValidException e){
        e.printStackTrace();
        // ((BeanPropertyBindingResult) e.bindingResult).errors.get(0).defaultMessage

        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        // map():对于流的一些中间业务操作就用此方法
        // ObjectError::getDefaultMessage:我只要流中对象的defaultMessage
        //errors.stream().map(o->o.getDefaultMessage())
        // Collectors.joining(","):joining只对字符串生效,用其他类型会报错,把所有的得到的字符串使用逗号进行隔开并组合成一个字符串
        String messages = errors.stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(","));

        return AjaxResult.error(GlobalExceptionEnum.PARAM_ERROR.getCode(), messages);
    }

    /**
     * 处理校验接口参数时候的异常ConstraintViolationException
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public AjaxResult globalExceptionHandler(ConstraintViolationException e){
        e.printStackTrace();
        return AjaxResult.error(GlobalExceptionEnum.PARAM_ERROR.getCode(), e.getMessage());
    }
}

支持JSR303校验规则实体类改造:

package com.noting.org.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

/**
 * 员工实体类employee
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {

    @NotNull(message = "id不能为空")
    private Long id;
    // 员工名称
    @NotBlank(message = "名称不能为空!")
    private String username;
    // 电话
    @NotBlank(message = "电话不能为空!")
    private String phone;
    // 邮箱
    @NotBlank(message = "邮箱不能为空!")
    @Email(message = "邮箱格式不正确!")
    private String email;
    // 密码盐值
    private String salt;
    // 密码
    private String password;
    // 年龄
    private Integer age;
    // 员工状态0:禁用,1:启用
    private Integer state;
    // 所属部门id
    private Long departmentId;
    // 登录信息id
    private Long logininfoId;
    // 店铺id
    private Long shopId;
}

全局异常处理类添加方法(校验相关异常):

如果没有定义明确的异常方法, 默认就去到了exception类型这边,
处理校验对象时候的异常MethodArgumentNotValidException
处理校验接口参数时候的异常ConstraintViolationException

一些测试的接口:

    //批量删除接口
    @ApiOperation(value = "批量删除接口")
    @PatchMapping
    public AjaxResult patchDel(@RequestBody List<Long> ids){
        if (ids == null || ids.isEmpty()){
            throw new GlobalException(GlobalExceptionEnum.DELETE_ERROR);
        }
        departmentService.patchDelete(ids);
        return AjaxResult.success();
    }

    /**
     * 查询部门树数据接口
     * @return
     */
    @ApiOperation(value = "查询部门树数据接口")
    @GetMapping("/tree")
    public List<Department> tree(){
        return departmentService.tree();
    }

    /**
     * 测试全局异常
     * @return
     */
    @GetMapping("/test/{id}")
    public AjaxResult test(@PathVariable("id") Long id){
//        int a = 1/0;
        if(id == 0) throw new GlobalException(GlobalExceptionEnum.PARAM_ERROR);
        if(id == 1) throw new GlobalException(GlobalExceptionEnum.DELETE_ERROR);
        return AjaxResult.success();
    }

    @PostMapping("/test")
    public AjaxResult test(@Valid @RequestBody Employee employee){
//        int a = 1/0;

        return AjaxResult.success();
    }

    @PostMapping("/test2/{age}")
    public AjaxResult test2(@Min(value = 18, message = "年龄不能小于18岁!") @PathVariable("age")Integer age) {
        return AjaxResult.success();
    }

    // localhost:8080/department/test2?age = xxxx, 下面这个方法不是restful风格,是普通地址传参
    @GetMapping("/test2")
    public AjaxResult test3(@Min(value = 18, message = "年龄不能小于18岁!") Integer age) {
        return AjaxResult.success();
    }


AjaxResult响应结果类的改造:

  • 补充了状态码
  • success和error的重载方法可以快速创建成功或者失败的结果类,定义了一些传入状态码和消息的传入方式
package com.noting.basic.utils;

import com.noting.basic.exception.GlobalExceptionEnum;
import lombok.Data;

/**
 * 返回响应结果类AjaxResult
 */
@Data
public class AjaxResult {

    // 默认成功
    private boolean success = true;

    // 默认成功的状态码为0
    private String code = "0";

    // 返回的消息,默认成功
    private String message = "操作成功!";

    // 保存任何数据类型的数据
    private Object data;



    public AjaxResult(){}

    public AjaxResult(boolean success, String message){
        this.success = success;
        this.message = message;
    }

    public AjaxResult(boolean success, String code, String message){
        this.success = success;
        this.code = code;
        this.message = message;
    }

    public AjaxResult(boolean success, String code, String message, Object data) {
        this.success = success;
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static AjaxResult success(){
        return new AjaxResult();
    }

    public static AjaxResult success(String code, String message){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setMessage(message);
        ajaxResult.setCode(code);
        return ajaxResult;
    }

    public static AjaxResult success(String code, String message, Object data){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setMessage(message);
        ajaxResult.setCode(code);
        ajaxResult.setData(data);
        return ajaxResult;
    }

    public static AjaxResult success(Object data){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setData(data);
        return ajaxResult;
    }

    public static AjaxResult error(GlobalExceptionEnum globalExceptionEnum){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setSuccess(false);
        ajaxResult.setMessage(globalExceptionEnum.getMessage());
        ajaxResult.setCode(ajaxResult.getCode());
        return ajaxResult;
    }

    public static AjaxResult error(String code, String message){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setSuccess(false);
        ajaxResult.setMessage(message);
        ajaxResult.setCode(code);
        return ajaxResult;
    }

    public static AjaxResult error(String code, String message, Object data){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setSuccess(false);
        ajaxResult.setMessage(message);
        ajaxResult.setCode(code);
        ajaxResult.setData(data);
        return ajaxResult;
    }




    // 构建链式语法
    public AjaxResult setSuccess(boolean success){
        this.success = success;
        return this;
    }

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

    public AjaxResult setCode(String code){
        this.code = code;
        return this;
    }

    public AjaxResult setResultObj(Object data){
        this.data = data;
        return this;
    }


}

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
项目详细功能参考项目演示内容即可项目优势:1、项目从零开始到完结 附带视频,源码以及相关辅助资料,适合学习使用,项目也可拿来即用。2、几乎全手写代码,功能流程详细 跟着可以独立完成,附带详细代码相关常见bug 和 调试解决方案,让大家学会跟踪快速解决问题。3、系统后端使用LayUI技术,对页面不精通的小伙伴也可以快速完成精美页面的设计及应用,支持统一后台管理,也可拿来做其他项目通用后台4、针对layui 相关技术点薄弱的学员提供相关技术点学习,让快速上手完成项目研发5、选材来自生活,项目真实感强,可用学习使用和就业面试使用,适合作为面试中提高实际项目经验...6、该项目前后端分离,满足前沿技术点..项目技术栈:- 数据库:MySQL8.0- 后端技术:SpringBoot,MyBatisPlus,JWT 等- 日志技术:Log4j- 数据库连接池:druid- 前端技术:LayUI, axios,Echarts,Ztree 等- Web容器:Apache Tomcat 9- 项目管理工具:Maven3.6- 思维导图设计工具:XMIND 8- 开发工具: IDEA2020, WebStorm2020- 数据库设计软件:Power Designer16.5特别提示:1、涉及相关技术点学习,更多侧重大学生或无项目经验以及项目经验较少的学员入门到项目完结项目实战2、项目中功能处理大多提供多种解决方式,如跨域访问,更多让大家了解解决方式的同时学会技术点应用3、加入bug的调试以及代码跟踪处理,更好的让学员更多学会如何解决问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值