To map all 5xx errors by using a FreeMarker template, your directory structure would be as follows:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.ftlh
+- <other templates>
在找不到精确的比如400.html 错误后会寻找4xx.html错误
DefaultErrorViewResolver.java
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
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.resources.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;
}
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "zero by gaojl 000!!!!!!!!!")
public class ZeroDividException extends RuntimeException {
public ZeroDividException(String message){
super(message);
}
}
这样当dispatcher 调度时候,
mv = ha.handle(processedRequest,response,mappedHandler.getHandler());
捕获到我们抛出的异常时候,就会调用异常处理器处理抛出的自定义异常。最后会发起“/error”请求BasicErrorController中的
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
返回ModeAndView,从而可以返回模板下、/error/500.html 的页面,由于在第一种方式中解释了DefaultErrorViewResolver 可以在不能精确匹配的情况下使用5xx.html代替响应。
所以我觉得原理可以总结如下:当我们自定义异常类使用@Responsestatus(value=Httpstatuscode,reason="")
注解标注时,调用handlerExceptionResolver(ResponseStatusExceptionResolver)解析,同时在请求域request中加入异常信息同时返回null模型视图对象,tomcat底层调用sendError 从而再次发起/error请求,BasicErrorController 创建ModeAndView 返回ModeAndView,然后进行处理得到View 渲染写给浏览器。
一般用来处理全局异常。
@Slf4j
@ControllerAdvice
public class MyException {
@ExceptionHandler({ArithmeticException.class})
public String zeroException(Exception ex){
return "success";
}
}
这样当HandlerExceptionResolver 解析时 得到ModeAndView 从而得到View 进而渲染到success页面。