今天和大家谈谈SpringMVC中如何统一处理异常。本篇文章我们还是以简单明了的风格和大家介绍。
我们使用SpringBoot来进行讲解,其版本为2.2.1.RELEASE。首先来谈一下SpringMVC和Http状态码的关系。
需要提到的是在SpringMVC中出现以下异常时,SpringMVC就会把这些异常自动转换成特定的Http状态码。异常和状态码的映射关系如下:
除此之外,我们还可以把我们自定义的异常也映射为HTTP状态码。达到这种目的的方式是用一个注解来标记这个我们自定义的异常,这个注解就是@ResponseStatus,用法如下:
@ResponseStatus标记异常类
@ResponseStatus注解的value属性表示返回的HTTP状态码,reason表示消息。当Controller抛出ParamException异常时,SpringMVC会返回一个400的HTTP状态码,因为HttpStatus.BAD_REQUEST的值是400。
抛出ParamException
请求index3路径之后浏览器会收到400的响应,如图:
type=Bad Request, status=400
那么如果我们把ParamException异常类的注解@ResponseStatus去掉,浏览器又会收到什么状态码呢?去掉后如图:
注释掉@ResponseStatus
浏览器得到的状态如下,我们可以从图中看到HTTP状态是500:
type=Internal Server Error, status=500
这就说明了我们是可以通过@ResponseStatus注解把ParamException异常映射为400状态码的。
在SpringMVC的开发过程中仅仅把特定的异常映射为HTTP状态码是远远不够的,我们还需要统一处理Controller中抛出的异常。那么如何实现呢?我们在这里提供2种方式,下面我们一一来看。
首先要说明的是@ExceptionHandler注解,这个注解是用来标记一个方法是异常处理器。这个注解标记的方法能够处理当前Controller中所有抛出的异常,如果把这个注解用在一个Controller的基类中,让所有的Controller都继承这个基类,那么这个被@ExceptionHandler注解标记的方法就能处理所有Controller抛出的异常了。
先看一下基类如何设置:
基类
package com.fri.audioengine.utils;
import org.springframework.web.bind.annotation.ExceptionHandler;
public class BaseExceptionController {
@ExceptionHandler(Exception.class)
void myExcptionHandler(Exception ex){
if(ex instanceof IllegalStateException){
System.out.println("io is");
}else if(ex instanceof NullPointerException){
System.out.println("null ex");
}
}
}
从图中可以看到这个基类非常简单,仅仅一个方法,而且这个方法被@ExceptionHandler注解标记,括号中的参数表示这个方法用于处理什么类型的异常。这个方法有一个参数,这个参数可以接收到抛出的异常。
继承基类
再让其他的具体的Controller继承这个基类就可以了,如图:
这样就完成了,在基类的myExceptionHandler方法中就可以统一处理所有Controller中抛出的异常了。
这种方式就是为Controller设置一个切面,在切面中处理异常。
定义切面
首先定义一个切面,定义切面的方式是在切面类上添加@ControllerAdvice注解就可以了,标记完切面之后再通过@ExceptionHandler注解定义异常处理方法,如图:
然后我们就可以定义控制器了,而且我们的控制器也不用继承基类控制器了,如图:
这样所有Controller抛出的异常都能被Controller的切面中的异常处理器捕获到了,是不是很简单呢?
今天简单和大家分享了如果统一处理Controller的异常,在这里简单总结一下,第一点就是定义处理异常的handler,这是必须的,而且无论你是通过基类处理还是通过切面处理这都是不能缺少的。在这两种方式中定义异常handler的方式都是通过@ExceptionHandler注解完成的。