SpringBoot的默认错误处理和自定义的默认处理机制
- 默认异常和错误的处理机制
SpringBoot中有默认的资源文件夹,即template文件夹和static文件夹,在这两个里面添加error文件夹,并为其中的文件取上特定的名字就可以让当页面出现错误时,自动跳转到错误的页面.如果要携带错误信息的话,放在模板引擎专用的template文件夹下。
演示:
随便输入一个不存在的页面.
原理解析:
此种情况下,异常将被DefaultErrorViewResolver和BasicErrorController配合解决:
private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName;//获取需要跳转的视图名
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
this.applicationContext);
if (provider != null) {
return new ModelAndView(errorViewName, model);
}
return resolveResource(errorViewName, model);//处理错误
}
//尝试去默认的默认资源路径下匹配解析出来的视图
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
for (String location : this.resourceProperties.getStaticLocations()) {
try {
Resource resource = this.applicationContext.getResource(location);
resource = resource.createRelative(viewName + ".html");
if (resource.exists()) {
return new ModelAndView(new HtmlResourceView(resource), model);
}
}
catch (Exception ex) {
}
}
return null;
}
@Override
//在这里可以找到错误的属性
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));
if (Boolean.TRUE.equals(this.includeException)) {
options = options.including(Include.EXCEPTION);
}
if (!options.isIncluded(Include.EXCEPTION)) {
errorAttributes.remove("exception");
}
if (!options.isIncluded(Include.STACK_TRACE)) {
errorAttributes.remove("trace");
}
if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {
errorAttributes.put("message", "");
}
if (!options.isIncluded(Include.BINDING_ERRORS)) {
errorAttributes.remove("errors");
}
return errorAttributes;
}
2.@ControllerAdvice和@ExceptionHandler注解配合使用
此种方法可以处理全局的异常,原理是这样可以把该类加入到可以处理异常的解析器队列中去。
@ControllerAdvice
public class FirstAdvice {
//代表可以处理数学运算异常,要想处理多个异常,可以自己添加。
@ExceptionHandler({ArithmeticException.class})
public ModelAndView resolveBasicException(HttpServletRequest request, HttpServletResponse response,Exception exception){
System.out.println(exception.getClass());
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error/4xx");
modelAndView.addObject("msg", exception.getMessage());
return modelAndView;
}
}
注意这里一定要返回一个ModelAndView对象,原因是SpringBoot底层会拿所有的异常解析器去尝试解析异常,解析是否成功的标准就是是否返回一个不为空的ModelAndView对象。
我们可以看看底层的这一段代码.
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
可以就按带如果处理之后返回的ModelAndView对象不为空,就退出循环,否则,循环继续.
3.@ResponseStatus注解配合自定以异常的使用。
@ResponseStatus(reason = "访问的页面不存在",code = HttpStatus.LOOP_DETECTED)
public class SecondAdvice extends RuntimeException{
public SecondAdvice() {
super();
}
public SecondAdvice(String message) {
super(message);
}
public SecondAdvice(String message, Throwable cause) {
super(message, cause);
}
public SecondAdvice(Throwable cause) {
super(cause);
}
protected SecondAdvice(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
- code属性和value属性可以混合使用,表示处理完成之后设置的响应状态码,根据状态码选择跳转到哪个视图。
- reason属性表示错误信息,可以通过thymeleaf模板引擎的表达式取出。
附上底层代码的关键部分如下:
protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
throws IOException {
if (!StringUtils.hasLength(reason)) {
response.sendError(statusCode);//设置获取到的状态码
}
else {
String resolvedReason = (this.messageSource != null ?
this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
reason);//获取错误信息
response.sendError(statusCode, resolvedReason);
}
return new ModelAndView();
}