业务需求背景:
需求说明:为了不在controller编写大量的try-catch代码,需要进行统一异常处理,同时要进行错误信息以及错误码的统一管理,建议使用枚举进行错误码封装。同时要求系统支持JSR303校验规则。
统一异常处理的思路:
- 创建一个全局的异常处理器(Global Exception Handler):定义一个全局的异常处理类(异常处理器),用于捕获和处理所有未被捕获的异常。
- 定义异常处理方法:在全局异常处理器中定义异常处理方法,用于处理不同类型的异常。可以根据异常的类型、错误代码、错误信息等来进行分类处理,这里用的是通过异常类型来分类,**@ExceptionHandler注解**标识这个方法处理哪个类型的异常:
- 注册全局异常处理器:将全局异常处理器注册到应用程序中,使其成为默认的异常处理机制。具体的注册方式取决于应用程序的架构和框架,可以是通过配置文件、注解或代码等方式进行注册,这个地方是用的注解的方式:@RestControllerAdvice复合注解中有一个**@ControllerAdvice注解**将控制所有的Controller层并拦截所有的异常
- 异常处理逻辑:这里处理的逻辑是将错误码信息封装到枚举类中,将对应的异常的枚举类中的错误码信息给到自定义的异常中,再封装到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;}}
全局异常的处理类:
- 全局异常的处理类**@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;}}
版权归原作者 三无鸢 所有, 如有侵权,请联系我们删除。