SpringBoot全局错误处理的两种方式:注解法和覆盖路径法
文章目录
引言
对于系统中的异常处理是一个大问题,需要考虑几个问题,文后回答
- 代码中的异常什么时候该抛,什么时候该捕获
- 系统内部的异常应该在什么位置捕获,controller,service还是dao层
- 异常应该怎么处理,捕获到异常后应该怎么返回信息
- 后台应该处理的错误范围是那些
SpringBoot全局错误处理第一种方法:注解法
注解
springboot提供两个注解来完成全局的错误处理:
- @ControllerAdvice
- 见名知意,控制器增强,就是用来增强controller的方法;
- 主要用来定义@ExceptionHandler,@InitBinder和@ModelAttribute方法(本文只讲ExceptionHandler,其他之后会写)
- 作用在类上,适用于所有使用@RequestMapping方法
- 相当于是一个针对controller层的AOP
- 主要属性:basePackages,用来确定作用范围:@ControllerAdvice(basePackages =
“com.zhao.omservice.controller”)
- @ExceptionHandler
- 见名知意,异常处理;用来定义异常处理的方法;
- 作用在方法上,和@ControllerAdvice配合使用(可以自己单独使用,作用在controller层的类方法上),用来配置controller层的异常处理,只针对抛出的异常
- 主要属性只有一个:value,值为一个包含异常class的数组:@ExceptionHandler(value = {NoSuchMethodError.class,NoClassDefFoundError.class}),属性标识用来处理那种异常的方法。
- 一个类里面可以有多个被@ExceptionHandler注释的方法
- 被注释的方法,如果没有定义注解的value值,可以在方法参数中额外定义一个类型为任意异常的参数
使用
创建一个专门做异常处理的类,类上注解@ControllerAdvice,内部有若干个被注解@ExceptionHandler注释的处理方法:
/** * 异常处理 */
@ControllerAdvice(basePackages = "com.zhao.omservice.controller")
public class MyExceptionHandler {
/**
* 处理IO异常
* @param e
* @return
*/
@ExceptionHandler
public ResponseEntity runTimeExceptionMatchHandle(
IOException e, HttpServletRequest request,
HttpServletResponse response){
System.out.println(request.getMethod());
System.out.println(response);
return return new ResponseEntity(new Object, HttpStatus.OK);
}
/**
* 处理异常
* @param e
* @return
*/
@ExceptionHandler
public ResponseEntity exceptionMatchHandle(Exception e){
if(e instanceof AccessException){
//做处理
}else if (e instanceof ...)
return return new ResponseEntity(new Object, HttpStatus.OK);
}
}
- 注解配置好要扫的包
- 方法内部做异常的处理方法,参数中的异常等同于注解中的配置,可以很细化,也可以比较泛化(注解和参数都有的话注解优先,参数可能会失效)
- 当出现异常时,spring 会优先找该异常最近的实现,然后将该异常以及其他servlet信息传给这个方法去处理。
- 这个形式处理自定义异常比较方便,对待自己已知的并细化给出返回信息
- 方法的返回类型必须是org.springframework.http.ResponseEntity,不然的话,会发现,接口最终返回的结果跟你想象得不一样,因为如果不是ResponseEntity,那么发生异常的时候还是会找 /error 路径下的方法。
- 不过要想返回自定义的返回数据类型也可以,需要在方法上加 @ResponseBody 注解,或者在类上加上 @RestController
SpringBoot全局错误处理第二种方法:覆盖路径法
此方法范围比较广,可以处理404错误
实现逻辑:
在Spring Boot中,Controller中抛出的异常默认交给了 /error(可以通过属性server.error.path覆盖)来处理,应用程序可以将/error映射到一个特定的Controller中处理来代替Spring Boot的默认实现,可以使用继承 AbstractErrorController 来统一处理系统的各种异常。
实现方法
- 继承 AbstractErrorController
- 实现默认方法 public String getErrorPath()
- 实现构造方法,并加上@Controller注解
- 在类的内部写一个具体的错误处理方法,并将该方法映射路径为("/error");如果修改过就使用修改过的错误处理路径。
- 示例代码如下:构造方法以及实现默认方法参考BasicErrorController,该类是Spring Boot默认的处理,可以参考该类来实现自己的
@Controller
public class MyErrorController extends AbstractErrorController{
//该属性可以做其他用,可以获取错误的相关信息,可以自行查看其方法
private final ErrorProperties errorProperties;
public MyErrorController(ErrorAttributes errorAttributes,
ServerProperties serverProperties{
super(errorAttributes);
this.errorProperties = serverProperties.getError();
}
@Override
public String getErrorPath() {
return errorProperties.getPath();
}
/**
*处理错误:处理错误返回JSON的
* @param request
* @return
*/
@RequestMapping("${server.error.path:${error.path:/error}}")
@ResponseBody
public ModelAndView error(HttpServletRequest request,HttpServletResponse response){
//做异常处理或者其他判断,可以根据请求头中的Accept属性是否为”application/json“,来区别回json或者渲染的界面
return Response.failure(ErrorCode.ERROR);
}
protected ErrorProperties getErrorProperties() {
return this.errorProperties;
}
}
- BasicErrorController的构造方法是(ErrorAttributes errorAttributes, ErrorProperties errorProperties)
但在运行的时候可能会报异常,ErrorProperties可以通过ServerProperties注入。 - 获取异常的方法:
//如果request流被提取过那么这个异常就可能获取不到,例如注解与路径覆盖法同时用
protected Throwable getCause(HttpServletRequest request){
Throwable error = (Throwable)request.getAttribute("javax.servlet.error.exception");
if (error!=null){
//MVC有可能会封装异常成ServletException,需要调用getCause获取真正的异常
while(error instanceof ServletException && error.getCause() !=null){
error = ((ServletException) error).getCause();
}
}
return error;
}
两种方式的区别
- 注解方法,原理基于AOP;路径覆盖方法,基于Control的HandleMapping和HandleAdapter
- 注解方法,可以在方法参数中直接获得异常;路径覆盖方法,获取异常要基于request,比较复杂
- 优先顺序:同时存在的话,注解先执行,覆盖路径的方法后执行(针对情况:如果没有设置返回参数类型(ResponseEntity)或者加注解ResponseBody,被注解方法处理过后,还会在经过路径覆盖方法的代码)
- ContollerAdvice只能拦截控制器中的异常,换言之,只能拦截500之类的异常,但是对于404这样不会进入控制器处理的异常不起作用
- 建议还是使用注解法
总结以及回答上面的方法
现在的SpringBoot封装好东西,用起来都比较简单化了,如果没那么多的业务需求用注解比较简单。
回单上面的问题:
-
代码中的异常什么时候该抛,什么时候该捕获
异常不能对业务,出现分支的,建议抛出去做处理直接返回,能够对业务起到分支的异常建议捕获做分支处理 -
系统内部的异常应该在什么位置捕获,controller,service还是dao层
捕获的异常建议都在service捕获,dao抛出,controller抛出,controller不做异常处理 -
异常应该怎么处理,捕获到异常后应该怎么返回信息
异常的信息返回,建议有一个统一的实体类如下,可以拟定状态码,甚至细化到每个异常的返回结果都不同
@Datapublic class Response<T> {
private String resMsg;
private int status;
private T data;
}
- 后台应该处理的错误范围是那些
根据业务需求,有些只针对接口调用,那么类似于404这样的错误就不会去管
以上就是我的一些寡谈,有错误欢迎指正