异常类-exception
在业务中,我们针对可以预期的错误,可以主动throws抛出,方便后续开发理解代码问题。
前面我们设计了通用返回类,它是一个封装类,主要是在controller层中最后拿到返回值进行封装这种情况居多:
- 例如用-1代表出现错误,在返回值到controller的最后用封装类封装了提示信息
而在业务层中如果也要实现这样一种返回我们自定义的业务状态码信息,我们就可以通过全局异常类+自定义异常状态码实现
BusinessException和ResultUtils两者本质来说是可以互相替换使用,例如在controller层中:
return ResultUtils.error(ErrorCode.xxxx)
//---->
throw new BusinessException(ErrorCode.xxx)
在抛出异常之后,程序将不会继续执行之后的操作
封装全局异常处理:
a.定义业务异常类
- 相对于java的异常类,支持更多字段
- 自定义构造函数,更灵活/快捷的设置字段
b.编写全局异常处理器作用:
- 捕获代码中所有的异常,内部消化,让前端得到更详细的业务报错/信息
- 同时屏蔽掉项目框架本身的异常(不暴露服务器内部状态),集中处理,比如记录日志
实现:
- Spring AOP:在调用方法前后进行额外的处理
- 全局请求日志和登录校验
自定义异常类-BusinessException
本质上也是包装类,它是在运行时异常RuntimeException上进行一次封装,类似于BaseResponse在其上,我们可以添加状态码
RuntimeException中已经包含返回的异常信息,所以不需要再定义返回信息,而我们要封装肯定也是要封装没有的,有的我们之后可以直接调就可以使用了。
这里可以搭配前面写好的ErrorCode,将其作为对象传入,以我们自定义的异常信息为主来显示
public class BusinessException extends RuntimeException{
/**
* 异常码
*/
private final int code;
public int getCode() {
return code;
}
public BusinessException(String message, int code) {
super(message);
this.code = code;
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
}
}
super:调用父类的构造方法。在这里是调用继承类RuntimeException的构造方法,将传入参数并进行封装
当然还可以封装一些其他属性,例如:description
description是更详细的信息,它主要是前端友好性,例如参数错误,具体到是什么参数原因导致的,当然在后端可以通过断点的形式直接确定是什么原因,但前端就比较麻烦了。
为什么继承运行时异常RuntimeException?
异常:
- 检查异常(Checked Exception)
- 非检查异常(Unchecked Exception)
类别 | 继承关系 | 特点 |
---|---|---|
检查异常(Checked Exception) | 继承自 Exception | - 在方法的声明中必须显式地声明可能抛出的异常。 - throws 子句声明- try-catch 捕获 |
非检查异常(Unchecked Exception) | 继承自 RuntimeException | - 在方法的声明中无需显式地声明可能抛出的异常。 - 不受编译器的检查,调用者可以选择是否处理异常。 |
我们要设计的自定义异常类也是属于非检查异常下的,所以这里是需要继承RuntimeException来实现,
全局异常处理类-GlobalExceptionHandler
全局异常类是自定义异常类的拓展,它将范围覆盖到代码中的所有异常。
利用Spring的aop切面编程思想来设计(拦截所有controller的请求),并可以进行分类处理。
例如针对业务异常(BusinessException)做对应的处理,也可以对其他异常(RuntimeException)做统一处理(记录错误日志)
我们主要使用@RestControllerAdvice来捕获Controller层中的所有异常(aop思想),用@ExceptionHandler分别对它们进行处理
-
@RestControllerAdvice: @ControllerAdvice + @ResponseBody ,它会拦截所有带有 @RestController 或 @Controller 注解的控制器,并将处理结果以 JSON 格式返回。
-
@ExceptionHandler:捕获特定类型的异常。当发生被注解的异常类型时,Spring 就会调用对应的处理方法来处理异常。
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public BaseResponse<?> businessExceptionHandler(BusinessException e) {
log.error("businessException: " + e.getMessage(), e);
return ResultUtils.error(e.getCode(), e.getMessage(), e.getDescription());
}
@ExceptionHandler(RuntimeException.class)
public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
log.error("runtimeException", e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage(), "");
}
}
加深印象,我们可以手动抛出异常来检测是否能拦截并做处理。
int a = 10/0;
– Java 中,除以零是一个不合法操作,会导致运行时异常(RuntimeException-不需要显式抛出)
在controller层中,写一个Get请求:
@GetMapping("/getById/{userId}")
public BaseResponse<User> getById(@PathVariable Integer userId){
// 手动抛出异常
int a = 10/0;
return ResultUtil.success(userService.getById(userId));
}
使用@PathVariable将模板变量(占位符userId)的值解析出来赋值给userId,之后将该值传给userService中的方法
对应的在页面中输入url,即可看到结果:
message:就是抛出异常打印的错误日志