SpringBoot全局错误处理的两种方式:注解法和覆盖路径法

SpringBoot全局错误处理的两种方式:注解法和覆盖路径法

引言

对于系统中的异常处理是一个大问题,需要考虑几个问题,文后回答

  1. 代码中的异常什么时候该抛,什么时候该捕获
  2. 系统内部的异常应该在什么位置捕获,controller,service还是dao层
  3. 异常应该怎么处理,捕获到异常后应该怎么返回信息
  4. 后台应该处理的错误范围是那些

SpringBoot全局错误处理第一种方法:注解法

注解

springboot提供两个注解来完成全局的错误处理:

  1. @ControllerAdvice
    • 见名知意,控制器增强,就是用来增强controller的方法;
    • 主要用来定义@ExceptionHandler,@InitBinder和@ModelAttribute方法(本文只讲ExceptionHandler,其他之后会写)
    • 作用在类上,适用于所有使用@RequestMapping方法
    • 相当于是一个针对controller层的AOP
    • 主要属性:basePackages,用来确定作用范围:@ControllerAdvice(basePackages =
      “com.zhao.omservice.controller”)
  2. @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);
    }
}

  1. 注解配置好要扫的包
  2. 方法内部做异常的处理方法,参数中的异常等同于注解中的配置,可以很细化,也可以比较泛化(注解和参数都有的话注解优先,参数可能会失效)
  3. 当出现异常时,spring 会优先找该异常最近的实现,然后将该异常以及其他servlet信息传给这个方法去处理。
  4. 这个形式处理自定义异常比较方便,对待自己已知的并细化给出返回信息
  5. 方法的返回类型必须是org.springframework.http.ResponseEntity,不然的话,会发现,接口最终返回的结果跟你想象得不一样,因为如果不是ResponseEntity,那么发生异常的时候还是会找 /error 路径下的方法。
  6. 不过要想返回自定义的返回数据类型也可以,需要在方法上加 @ResponseBody 注解,或者在类上加上 @RestController

SpringBoot全局错误处理第二种方法:覆盖路径法

此方法范围比较广,可以处理404错误

实现逻辑:

在Spring Boot中,Controller中抛出的异常默认交给了 /error(可以通过属性server.error.path覆盖)来处理,应用程序可以将/error映射到一个特定的Controller中处理来代替Spring Boot的默认实现,可以使用继承 AbstractErrorController 来统一处理系统的各种异常。

实现方法

  1. 继承 AbstractErrorController
  2. 实现默认方法 public String getErrorPath()
  3. 实现构造方法,并加上@Controller注解
  4. 在类的内部写一个具体的错误处理方法,并将该方法映射路径为("/error");如果修改过就使用修改过的错误处理路径。
  5. 示例代码如下:构造方法以及实现默认方法参考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;
}

两种方式的区别

  1. 注解方法,原理基于AOP;路径覆盖方法,基于Control的HandleMapping和HandleAdapter
  2. 注解方法,可以在方法参数中直接获得异常;路径覆盖方法,获取异常要基于request,比较复杂
  3. 优先顺序:同时存在的话,注解先执行,覆盖路径的方法后执行(针对情况:如果没有设置返回参数类型(ResponseEntity)或者加注解ResponseBody,被注解方法处理过后,还会在经过路径覆盖方法的代码)
  4. ContollerAdvice只能拦截控制器中的异常,换言之,只能拦截500之类的异常,但是对于404这样不会进入控制器处理的异常不起作用
  5. 建议还是使用注解法

总结以及回答上面的方法

现在的SpringBoot封装好东西,用起来都比较简单化了,如果没那么多的业务需求用注解比较简单。

回单上面的问题:

  1. 代码中的异常什么时候该抛,什么时候该捕获
    异常不能对业务,出现分支的,建议抛出去做处理直接返回,能够对业务起到分支的异常建议捕获做分支处理

  2. 系统内部的异常应该在什么位置捕获,controller,service还是dao层
    捕获的异常建议都在service捕获,dao抛出,controller抛出,controller不做异常处理

  3. 异常应该怎么处理,捕获到异常后应该怎么返回信息
    异常的信息返回,建议有一个统一的实体类如下,可以拟定状态码,甚至细化到每个异常的返回结果都不同

@Datapublic class Response<T> {    

private String resMsg;   
private int status;    
private T data;
}


  1. 后台应该处理的错误范围是那些
    根据业务需求,有些只针对接口调用,那么类似于404这样的错误就不会去管

以上就是我的一些寡谈,有错误欢迎指正

猜你喜欢

转载自blog.csdn.net/qq_38371367/article/details/105531298
今日推荐