项目中的全局异常是如何处理的?
全局异常处理逻辑
一般项目开发有两种异常:
- 预期异常(程序员手动抛出)
- 运行时异常
在目前的项目中已经提供了全局异常处理器
- BaseException 基础异常,如果业务中需要手动抛出异常,则需要抛出该异常
package com.zzyl.exception; import com.zzyl.enums.BasicEnum; import lombok.Getter; import lombok.Setter; /** * BaseException * @author itheima **/ @Getter @Setter public class BaseException extends RuntimeException { private BasicEnum basicEnum; public BaseException(BasicEnum basicEnum) { this.basicEnum = basicEnum; } }
其中BaseException中的参数为一个枚举,可以在BasicEnum自定义业务中涉及到的异常
package com.zzyl.enums; import com.zzyl.base.IBasicEnum; import lombok.AllArgsConstructor; import lombok.Getter; /** * 基础枚举 * * @author itcast */ @Getter @AllArgsConstructor public enum BasicEnum implements IBasicEnum { SUCCEED(200, "操作成功"), SECURITY_ACCESSDENIED_FAIL(401, "权限不足!"), LOGIN_FAIL(401, "用户登录失败"), LOGIN_LOSE_EFFICACY(401, "登录状态失效,请重新登录"), SYSYTEM_FAIL(500, "系统运行异常"), //权限相关异常:1400-1499 DEPT_DEPTH_UPPER_LIMIT(1400, "部门最多4级"), PARENT_DEPT_DISABLE(1401, "父级部门为禁用状态,不允许启用"), DEPT_NULL_EXCEPTION(1402, "部门不能为空"), POSITION_DISTRIBUTED(1403, "职位已分配,不允许禁用"), MENU_NAME_DUPLICATE_EXCEPTION(1404, "菜单路由重复"), //业务相关异常:1500-1599 WEBSOCKET_PUSH_MSG_ERROR(1500, "websocket推送消息失败"), CLOSE_BALANCE_ERROR(1501, "关闭余额账户失败"), MONTH_BILL_DUPLICATE_EXCEPTION(1502, "该老人的月度账单已生成,不可重复生成"), MONTH_OUT_CHECKIN_TERM(1503, "该月不在费用期限内"); /** * 编码 */ public final int code; /** * 信息 */ public final String msg; }
- GlobalExceptionHandler 全局异常处理器
package com.zzyl.exception; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import com.zzyl.base.ResponseResult; import com.zzyl.enums.BasicEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DuplicateKeyException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.multipart.MaxUploadSizeExceededException; import java.io.FileNotFoundException; import java.nio.file.AccessDeniedException; @RestControllerAdvice @Slf4j public class GlobalExceptionHandler { /** * 处理自定义异常BaseException。 * 返回自定义异常中的错误代码和错误消息。 * * @param exception 自定义异常 * @return 响应数据,包含错误代码和错误消息 */ @ExceptionHandler(BaseException.class) public ResponseResult<Object> handleBaseException(BaseException exception) { exception.printStackTrace(); if (ObjectUtil.isNotEmpty(exception.getBasicEnum())) { log.error("自定义异常处理:{}", exception.getBasicEnum().getMsg()); } return ResponseResult.error(exception.getBasicEnum()); } /** * 处理其他未知异常。 * 返回HTTP响应状态码500,包含错误代码和异常堆栈信息。 * * @param exception 未知异常 * @return 响应数据,包含错误代码和异常堆栈信息 */ @ExceptionHandler(Exception.class) public ResponseResult<Object> handleUnknownException(Exception exception) { exception.printStackTrace(); if (ObjectUtil.isNotEmpty(exception.getCause())) { log.error("其他未知异常:{}", exception.getMessage()); } return ResponseResult.error(500,exception.getMessage()); } }
项目中集成
程序员手动抛出业务异常
当床位新增失败的时候,可以直接抛出BaseException
@Override public void addBed(BedDto bedDto) { Bed bed = BeanUtil.toBean(bedDto, Bed.class); bed.setCreateTime(LocalDateTime.now()); bed.setCreateBy(1L); bed.setBedStatus(0); try { bedMapper.addBed(bed); } catch (Exception e) { throw new BaseException(BasicEnum.BED_INSERT_FAIL); } }
测试:
当重复录入床位编号的时候,则会抛出:床位新增失败,而此时的执行逻辑就是走了全局异常处理器
不可知异常处理
比如,在文件上传的接口中,如果上传文件失败,则可以抛出RuntimeException异常,由于RuntimeException异常不是自定义异常,一旦触发就是走全局异常处理器的未知异常
/** * 文件上传 * * @param file 文件 * @return 上传结果 * @throws Exception 异常 */ @PostMapping("/upload") @ApiOperation("文件上传") public ResponseResult<String> upload( @ApiParam(value = "上传的文件", required = true) @RequestPart("file") MultipartFile file) throws Exception { // 校验是否为图片文件 try { BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); } catch (Exception e) { throw new RuntimeException("上传图片失败"); } if (file.getSize() == 0) { throw new RuntimeException("上传图片不能为空"); } // 获得原始文件名 String originalFilename = file.getOriginalFilename(); // 获得文件扩展名 String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); String fileName = UUID.randomUUID().toString() + extension; String filePath = fileStorageService.store(fileName, file.getInputStream()); return ResponseResult.success(filePath); }
结论:
- 一旦文件上传失败,则会走全局异常处理器的未知异常
- 如果系统抛出了其他异常,非BaseException,都会走未知异常