1.先理解为什么要抛出异常?
一句话就是为了终止程序,一般是终止业务层也就是service层。
2.为什么要自定义异常抛出?
因为系统提供的异常种类很多,而且代表的含义很多,所以我们需要自己定义一个通用的异常,然后只要是业务层的异常我都抛出这个,只是给它不同的提示信息进行区别。其实大部分业务层抛出异常只是为了快速终止程序,提示给用户到底发生了什么错误。
3.为什么要进行全局异常捕获处理?
(1)如果不捕获当然也可以,但是很不友好,第一请求的响应结果是一个错误页面的标签,并且在控制台会直接打印出一堆很乱的错误信息的追踪。如图:
(2)所以一般情况下还是要捕获,但是很多人可能好奇,try catch不是能捕获吗?虽然能捕获但是不能终止异常,它后面的代码依然会执行,并且全局的异常也没办法做,一般只有编译器异常才会用try catch 例如文件找不到,因为不try catch程序一直报错,一般这种我们也是在catch里面重新抛出我们自定义的异常,然后再进行全局异常捕获。
(3)全局异常捕获的好处,第一可以指定捕获到的全局异常,然后还可以定义响应码和响应结果进行返回,不会出现刚才响应一个html标签奇怪的东西,第二不会在控制台打印出那个异常的错误信息,也很友好。
4.项目中如何自定义异常和全局捕获(实战)
(1)自定义业务层异常ServiceException放在exception包下面
一般包括错误码code和msg,此处多了一个响应枚举ResponseEnum,是因为有些错误很常见,可以统一提前定义在枚举里面,以后写的时候可以直接new throw ServiceException(ResponseEnum.xxx)更方便一些,当然一般情况下还是直接传入code和msg
package com.fzy.exception;
import com.fzy.constant.ResponseEnum;
import lombok.Data;
@Data
public class ServiceException extends RuntimeException {
private ResponseEnum responseEnum;
private Integer code;
private String msg;
public ServiceException(ResponseEnum responseEnum) {
super(responseEnum.getMessage());
this.responseEnum = responseEnum;
this.code = responseEnum.getCode();
this.msg = responseEnum.getMessage();
}
public ServiceException(Integer code, String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
public ServiceException(String msg) {
super(msg);
this.code = ResponseEnum.FAILURE.getCode();
this.msg = msg;
}
}
(2)响应的异常枚举ResponseEnum
package com.fzy.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum ResponseEnum {
SUCCESS(200,"成功"),
FAILURE(500,"服务器错误"),
FILE_SIZE_ERROR(500, "文件大小异常");
private Integer code;
private String message;
}
(3)返回给前端的ResponseVO
package com.fzy.result;
import com.fzy.constant.ResponseEnum;
import lombok.Data;
@Data
public class ResponseVO<T> {
private Integer code;
private String message;
private T data;
public ResponseVO(ResponseEnum resultEnum) {
this.code = resultEnum.getCode();
this.message = resultEnum.getMessage();
}
public ResponseVO(Integer code, String message) {
this.code = code;
this.message = message;
}
public ResponseVO(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public ResponseVO(T data) {
this.data = data;
}
}
(4)全局异常处理类SystemGlobalExceptionHandler,放到handler包下面
其中@ExceptionHandler主要是表明捕获哪个异常,可以给参数进行指定,e.getMessage是它父类的方法,并且我们在 new ServiceException时调用的super方法就是给父类中的Message赋值。
package com.fzy.handler;
import com.fzy.exception.ServiceException;
import com.fzy.result.ResponseVO;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class SystemGlobalExceptionHandler {
@ExceptionHandler(value = RuntimeException.class)
public ResponseVO exceptionHandler(ServiceException e){
System.out.println(e.getMessage());
if (e.getResponseEnum() != null) {
return new ResponseVO<>(e.getResponseEnum());
} else {
return new ResponseVO<>(e.getCode(), e.getMsg());
}
}
}
总结:上面这种写法是为了更好的封装和规范开发,多理解一下,一般只在Service层进行抛出异常终止程序。