0


【SpringBoot】统一异常处理

业务需求背景:

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

统一异常处理的思路:

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

代码:

异常类:

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

packagecom.noting.basic.exception;importlombok.Data;/**
 * 自定义全局异常类GlobalException
 */@DatapublicclassGlobalExceptionextendsRuntimeException{privateString code;privateString globalMessage;publicGlobalException(){}publicGlobalException(String message){super(message);}publicGlobalException(String code,String message){super(message);this.code = code;this.globalMessage = message;}// 建立直接传入枚举做参数可以直接创建对应的异常publicGlobalException(GlobalExceptionEnum globalExceptionEnum){super(globalExceptionEnum.getMessage());this.code = globalExceptionEnum.getCode();this.globalMessage = globalExceptionEnum.getMessage();}}

枚举类封装错误码信息:

packagecom.noting.basic.exception;importlombok.Getter;/**
 * 枚举类封装错误码信息:
 */@GetterpublicenumGlobalExceptionEnum{// 1. 字段(枚举实例)ERROR("-1","系统异常,请稍后再试!"),SUCCESS("0","操作成功!"),

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

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

    DELETE_ERROR ("1101","删除错误"),UPLOAD_ERROR("1102","上传文件失败错误"),;// 2. 枚举实例privateString code;// 错误码privateString message;// 错误码对应的错误信息提示语// 3. 字段的构造方法GlobalExceptionEnum(String code,String message){this.code = code;this.message = message;}}

全局异常的处理类:

image.png

  • 全局异常的处理类**@RestControllerAdvice** *** **@RestControllerAdvice 该注解是一个复合注解包括下面两个注解 - **@ControllerAdvice ** 将控制所有的Controller层并拦截所有的异常- @ResponseBody 将本类中所有方法的返回值转换为JSON 相当于每次抛出异常就进入这个地方操作 注意点: 异常的类型不同, 需求不同可以继续添加
packagecom.noting.basic.exception;importcom.noting.basic.utils.AjaxResult;importorg.springframework.validation.ObjectError;importorg.springframework.web.bind.MethodArgumentNotValidException;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.RestControllerAdvice;importjavax.validation.ConstraintViolationException;importjava.util.List;importjava.util.stream.Collectors;/**
 * 全局异常的处理器@RestControllerAdvice
 *  @RestControllerAdvice 该注解是一个复合注解包括下面两个注解
    *  @ControllerAdvice  将控制所有的Controller层并拦截所有的异常
    *  @ResponseBody       将本类中所有方法的返回值转换为JSON
 */@RestControllerAdvice// 必须必须要被启动类扫描publicclassGlobalExceptionHandler{/**
     * 处理系统异常
     * @return
     *
     * catch (Exception e) {
     *             e.printStackTrace();
     *             return AjaxResult.me().setSuccess(false).setMessage("系统繁忙,请重试!");
     *         }
     */@ExceptionHandler(Exception.class)//这句代码可以认为是trycatch中的catchpublicAjaxResultexceptionHandler(Exception e){
        e.printStackTrace();returnAjaxResult.error(GlobalExceptionEnum.ERROR.getCode(),GlobalExceptionEnum.ERROR.getMessage());}/**
     * 处理自定义业务异常
     * @return
     *
     * catch (GlobalException e) {
     *             e.printStackTrace();
     *             return AjaxResult.me().setSuccess(false).setMessage("系统繁忙,请重试!");
     *         }
     */@ExceptionHandler(GlobalException.class)//这句代码可以认为是trycatch中的catchpublicAjaxResultglobalExceptionHandler(GlobalException e){
        e.printStackTrace();returnAjaxResult.error(e.getCode(), e.getGlobalMessage());}/**
     * 处理校验对象时候的异常MethodArgumentNotValidException
     * @param e
     * @return
     */@ExceptionHandler(MethodArgumentNotValidException.class)publicAjaxResultglobalExceptionHandler(MethodArgumentNotValidException e){
        e.printStackTrace();// ((BeanPropertyBindingResult) e.bindingResult).errors.get(0).defaultMessageList<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(","));returnAjaxResult.error(GlobalExceptionEnum.PARAM_ERROR.getCode(), messages);}/**
     * 处理校验接口参数时候的异常ConstraintViolationException
     * @param e
     * @return
     */@ExceptionHandler(ConstraintViolationException.class)publicAjaxResultglobalExceptionHandler(ConstraintViolationException e){
        e.printStackTrace();returnAjaxResult.error(GlobalExceptionEnum.PARAM_ERROR.getCode(), e.getMessage());}}

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

packagecom.noting.org.domain;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importjavax.validation.constraints.Email;importjavax.validation.constraints.NotBlank;importjavax.validation.constraints.NotNull;/**
 * 员工实体类employee
 */@Data@NoArgsConstructor@AllArgsConstructorpublicclassEmployee{@NotNull(message ="id不能为空")privateLong id;// 员工名称@NotBlank(message ="名称不能为空!")privateString username;// 电话@NotBlank(message ="电话不能为空!")privateString phone;// 邮箱@NotBlank(message ="邮箱不能为空!")@Email(message ="邮箱格式不正确!")privateString email;// 密码盐值privateString salt;// 密码privateString password;// 年龄privateInteger age;// 员工状态0:禁用,1:启用privateInteger state;// 所属部门idprivateLong departmentId;// 登录信息idprivateLong logininfoId;// 店铺idprivateLong shopId;}

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

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

一些测试的接口:

//批量删除接口@ApiOperation(value ="批量删除接口")@PatchMappingpublicAjaxResultpatchDel(@RequestBodyList<Long> ids){if(ids ==null|| ids.isEmpty()){thrownewGlobalException(GlobalExceptionEnum.DELETE_ERROR);}
        departmentService.patchDelete(ids);returnAjaxResult.success();}/**
     * 查询部门树数据接口
     * @return
     */@ApiOperation(value ="查询部门树数据接口")@GetMapping("/tree")publicList<Department>tree(){return departmentService.tree();}/**
     * 测试全局异常
     * @return
     */@GetMapping("/test/{id}")publicAjaxResulttest(@PathVariable("id")Long id){//        int a = 1/0;if(id ==0)thrownewGlobalException(GlobalExceptionEnum.PARAM_ERROR);if(id ==1)thrownewGlobalException(GlobalExceptionEnum.DELETE_ERROR);returnAjaxResult.success();}@PostMapping("/test")publicAjaxResulttest(@Valid@RequestBodyEmployee employee){//        int a = 1/0;returnAjaxResult.success();}@PostMapping("/test2/{age}")publicAjaxResulttest2(@Min(value =18, message ="年龄不能小于18岁!")@PathVariable("age")Integer age){returnAjaxResult.success();}// localhost:8080/department/test2?age = xxxx, 下面这个方法不是restful风格,是普通地址传参@GetMapping("/test2")publicAjaxResulttest3(@Min(value =18, message ="年龄不能小于18岁!")Integer age){returnAjaxResult.success();}

AjaxResult响应结果类的改造:

  • 补充了状态码
  • success和error的重载方法可以快速创建成功或者失败的结果类,定义了一些传入状态码和消息的传入方式
packagecom.noting.basic.utils;importcom.noting.basic.exception.GlobalExceptionEnum;importlombok.Data;/**
 * 返回响应结果类AjaxResult
 */@DatapublicclassAjaxResult{// 默认成功privateboolean success =true;// 默认成功的状态码为0privateString code ="0";// 返回的消息,默认成功privateString message ="操作成功!";// 保存任何数据类型的数据privateObject data;publicAjaxResult(){}publicAjaxResult(boolean success,String message){this.success = success;this.message = message;}publicAjaxResult(boolean success,String code,String message){this.success = success;this.code = code;this.message = message;}publicAjaxResult(boolean success,String code,String message,Object data){this.success = success;this.code = code;this.message = message;this.data = data;}publicstaticAjaxResultsuccess(){returnnewAjaxResult();}publicstaticAjaxResultsuccess(String code,String message){AjaxResult ajaxResult =newAjaxResult();
        ajaxResult.setMessage(message);
        ajaxResult.setCode(code);return ajaxResult;}publicstaticAjaxResultsuccess(String code,String message,Object data){AjaxResult ajaxResult =newAjaxResult();
        ajaxResult.setMessage(message);
        ajaxResult.setCode(code);
        ajaxResult.setData(data);return ajaxResult;}publicstaticAjaxResultsuccess(Object data){AjaxResult ajaxResult =newAjaxResult();
        ajaxResult.setData(data);return ajaxResult;}publicstaticAjaxResulterror(GlobalExceptionEnum globalExceptionEnum){AjaxResult ajaxResult =newAjaxResult();
        ajaxResult.setSuccess(false);
        ajaxResult.setMessage(globalExceptionEnum.getMessage());
        ajaxResult.setCode(ajaxResult.getCode());return ajaxResult;}publicstaticAjaxResulterror(String code,String message){AjaxResult ajaxResult =newAjaxResult();
        ajaxResult.setSuccess(false);
        ajaxResult.setMessage(message);
        ajaxResult.setCode(code);return ajaxResult;}publicstaticAjaxResulterror(String code,String message,Object data){AjaxResult ajaxResult =newAjaxResult();
        ajaxResult.setSuccess(false);
        ajaxResult.setMessage(message);
        ajaxResult.setCode(code);
        ajaxResult.setData(data);return ajaxResult;}// 构建链式语法publicAjaxResultsetSuccess(boolean success){this.success = success;returnthis;}publicAjaxResultsetMessage(String message){this.message = message;returnthis;}publicAjaxResultsetCode(String code){this.code = code;returnthis;}publicAjaxResultsetResultObj(Object data){this.data = data;returnthis;}}
标签: java 数据库 spring

本文转载自: https://blog.csdn.net/qq_37038626/article/details/131270658
版权归原作者 三无鸢 所有, 如有侵权,请联系我们删除。

“【SpringBoot】统一异常处理”的评论:

还没有评论