SpringBoot异常自动处理机制
ErrorMvcAutoConfiguration中注入了四个重要的类,分别是
DefaultErrorAttributes(用处共享页面中的数据)、BasicErrorController、ErrorPageCustomizer、DefaultErrorViewResolver四个类。
当发生错误时,
1、ErrorPageCustomer会将请求转发到/error地址中,
2、然后通过BasicErrorController来处理,最终的处理结果是返回一个ModelAndView(指定了转发的路径和需要渲染的数据)
这里SpringBoot是通过请求头中的信息来辨别是浏览器还是客户端
如果是浏览器请求则请求BasicErrorController.errorHtml方法,响应的是一个ModelAndView。
如果是客户端请求则请求BasicErrorController.error方法,响应的是JSON形式的数据
(这里以浏览器请求为例子)
2.1、调用BasicErrorController.errorHtml(HttpServletRequest request,HttpServletResponse response)方法
@RequestMapping(produces="text/html")public ModelAndViewerrorHtml(HttpServletRequest request,
HttpServletResponse response){
HttpStatus status=getStatus(request);// 此处调用了getErrorAttributes来得到model,这里的getErrorAttributes就是之前注入的四个Bean之一,DefaultErrorAttributes中的方法。此方法中可以获取状态码、时间戳、错误提示等
Map<String, Object> model= Collections.unmodifiableMap(getErrorAttributes(
request,isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView=resolveErrorView(request, response, status, model);return(modelAndView== null?newModelAndView("error", model): modelAndView);}
2.2、errorHtml方法中调用了AbstractErrorController.resolveErrorView(HttpServletRequest request,HttpServletResponse response, HttpStatus status, Map<String, Object> model)方法。
// AbstractErrorController.resolveErrorView方法protected ModelAndViewresolveErrorView(HttpServletRequest request,
HttpServletResponse response, HttpStatus status, Map<String, Object> model){// (注意:这里的ErrorMvcAutoConfiguration.errorViewResolvers和AbstractErrorController.errorViewResolvers是同一个,是ErrorMvcAutoConfiguration在注入Bean的时候通过构造方法传入的)for(ErrorViewResolver resolver:this.errorViewResolvers){// 遍历所有的视图解析器,找到适合的解析器// 这里调用了DefaultErrorViewResolver.resolveErrorView方法
ModelAndView modelAndView= resolver.resolveErrorView(request, status, model);if(modelAndView!= null){return modelAndView;}}// 如果所有的视图解析器都无法解析则返回nullreturn null;}
2.3、在遍历所有视图解析器的时候调用了DefaultErrorViewResolver.resolveErrorView方法
// DefaultErrorViewResolver.resolveErrorView@Overridepublic ModelAndViewresolveErrorView(HttpServletRequest request, HttpStatus status,
Map<String, Object> model){// 首先传入具体的状态码来解析,如404
ModelAndView modelAndView=resolve(String.valueOf(status), model);// 如果视图为空,并且错误代码为4xx或者5xx则传入状态码和model进行解析if(modelAndView== null&& SERIES_VIEWS.containsKey(status.series())){
modelAndView=resolve(SERIES_VIEWS.get(status.series()), model);}return modelAndView;}// DefaultErrorViewResolver.resolve方法private ModelAndViewresolve(String viewName, Map<String, Object> model){// 根据传入的状态码去 "error/" 下寻找是否有对应页面
String errorViewName="error/"+ viewName;// 模板引擎可以解析这个页面地址就用模板引擎解析
TemplateAvailabilityProvider provider=this.templateAvailabilityProviders.getProvider(errorViewName,this.applicationContext);if(provider!= null){// 模板引擎可用的情况下返回到errorViewName指定的视图地址returnnewModelAndView(errorViewName, model);}// 模板引擎不可用则在静态资源文件夹下寻找errorViewName对应的页面returnresolveResource(errorViewName, model);}// 去静态资源文件夹下找对应的页面,找不到则返回nullprivate ModelAndViewresolveResource(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()){returnnewModelAndView(newHtmlResourceView(resource), model);}}catch(Exception ex){}}return null;}
2.4、注意看2.1步骤中的代码,如果最后解析返回一个ModelAndView则会直接渲染后响应给请求,如果返回的是null,则会生成一个名为 “error” 的 ModelAndView ,也就是我们熟知的默认错误页面。
@Configuration@ConditionalOnProperty(prefix="server.error.whitelabel", name="enabled", matchIfMissing=true)@Conditional(ErrorTemplateMissingCondition.class)protectedstaticclassWhitelabelErrorViewConfiguration{privatefinal SpelView defaultErrorView=newSpelView("<html><body><h1>Whitelabel Error Page</h1>"+"<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>"+"<div id='created'>${timestamp}</div>"+"<div>There was an unexpected error (type=${error}, status=${status}).</div>"+"<div>${message}</div></body></html>");@Bean(name="error")@ConditionalOnMissingBean(name="error")public ViewdefaultErrorView(){returnthis.defaultErrorView;}// If the user adds @EnableWebMvc then the bean name view resolver from// WebMvcAutoConfiguration disappears, so add it back in to avoid disappointment.@Bean@ConditionalOnMissingBean(BeanNameViewResolver.class)public BeanNameViewResolverbeanNameViewResolver(){
BeanNameViewResolver resolver=newBeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE-10);return resolver;}}
如何定制错误的响应信息
定制错误页面
在上面也2.3中有分析到DefaultErrorViewResolver中视图解析器是如何解析的,**有模板引擎的情况下直接将对应错误状态码的html页面放入error文件夹下即可。**也可以使用 “4xx.html” 或者"5xx.html" 来处理一种类型的错误。没有模板引擎则直接去静态资源文件夹下找,如果静态资源也找不到则返回SpringBoot的默认错误页面