文章目录
异常处理
类似于springboot大部分的设计,当有异常发生时,springboot会在底层寻找所有的类型为HandlerExceptionResolver的组件来处理异常,核心源码:
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
}
else if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
springboot循环调用所有的handlerExceptionResolvers的resolveException方法,resolveException返回类型为ModelAndView对象,一旦有哪个resolver处理之后返回值不为null,则视为已经异常已被处理,默认有ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver三个处理器(并按照这个顺序循环调用)。
自定义错误显示页面
默认情况下,在浏览器发起一个text/html请求时发生错误,springboot会返回一个白页显示所有的错误信息,我们不想将这些错误信息暴露给前端,想定义一个异常页面,我们可以在视图文件夹,**新建对应错误状态码为名称的页面,如:404.html,500.html等等,当发生对应状态码的异常时,springboot会自动的跳转到对应的视图页面。或者也可以创建名为4xx和5xx的页面视图,当发生4开头的错误,就会跳转到4xx,发生5开头的错误就会跳转到5xx。**当然要在项目添加了视图解析的情况下。
自定义异常映射(springboot没有找到合适处理的异常处理器的情况下)
如果说springboot默认的三个处理器都无法处理异常,就会将请求转发到/error进行处理,springboot默认的定义了一个名为BasicErrorController来处理/error的映射,BasicErrorController在默认情况下如果浏览器发过来的请求发生错误就返回白页,机器客户端请求错误就返回一个json。如果我们想自定义一个全局的异常处理的话,可以实现一个名为ErrorController的接口并将其注册到容器中来替换默认的BasicErrorController,例如:
@RestController
public class TestErrorController implements ErrorController {
@ResponseBody
@RequestMapping("/error")
public String test(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
return "error" + statusCode;
}
}
springboot将异常的详细信息都存储在了request中,如果我们想拿到更详细的信息可以从request中取
使用@ControllerAdvice+@ExceptionHandler
对应ExceptionHandlerExceptionResolver处理器
ExceptionHandlerExceptionResolver默认在异常处理器第一个被调用,它会寻找所有的@ExceptionHandler注解声明的异常处理方法,根据异常类型匹配寻找合适的方法进行处理,
@Slf4j
@ControllerAdvice(basePackageClasses = TestController.class)
public class MyExceptionAdvice {
@ResponseBody
@ExceptionHandler(ArithmeticException.class)
public String handlerArithmeticException(HttpServletRequest request, Throwable ex) {
log.info("处理异常" + ex.getMessage());
return ex.getMessage();
}
}
使用@ResponseStatus
对应ResponseStatusExceptionResolver处理器
当发生的异常没有合适的@ExceptionHandler声明的方法处理(比如一些自定义异常),但根据异常的类型匹配到被@ResponseStatus声明的异常类,ResponseStatusExceptionResolver将会将定义在@ResponseStatus的信息添加到response中,最后在被转发到/error映射上
@ResponseStatus(code = HttpStatus.BAD_REQUEST,reason = "我的异常")
public class MyException extends RuntimeException{
public MyException(String message) {
super(message);
}
}
springboot底层注解的异常
对应DefaultHandlerExceptionResolver处理器
如果说抛出的异常属于springboot自己定义的异常,将会被DefaultHandlerExceptionResolver处理,默认时将错误信息添加到到response中,然后转发到/error映射中,如果在@ControllerAdvice+@ExceptionHandler中配置了处理方法,@ExceptionHandle将被优先执行。
自定义HandlerExceptionResolver
异常处理器是处理异常的基本单元,如果我们要自定义一些异常处理器,可以通过实现HandlerExceptionResolver接口并将其注册到容器中,需要注意的是,我们需要设置容器的优先级@Order确保我们自定义的处理器在springboot默认的处理器之前调用,否则可能异常被默认处理器处理而使得自定义处理器没有生效。
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
@SneakyThrows
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.write("error:"+ex.getMessage());
return new ModelAndView();
}
}