错误处理原理(自动配置解析篇)

2022-08-21 10:57:39

ErrorMvcAutoConfiguration里自动配置了异常处理规则。

DefaultErrorAttributes

ErrorMvcAutoConfiguration往容器中放入了一个DefaultErrorAttributes类型的组件,组件id为errorAttributes(即方法名为组件id)。

@Bean@ConditionalOnMissingBean(value=ErrorAttributes.class, search=SearchStrategy.CURRENT)publicDefaultErrorAttributeserrorAttributes(){returnnewDefaultErrorAttributes();}

DefaultErrorAttributes决定了响应中会包含哪些内容,如status、error、path、timestamp等。
DefaultErrorAttributes也实现了接口类HandlerExceptionResolver,因此也是一个处理器异常解析器

publicclassDefaultErrorAttributesimplementsErrorAttributes,HandlerExceptionResolver,Ordered{@OverridepublicModelAndViewresolveException(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex){storeErrorAttributes(request, ex);returnnull;}privatevoidstoreErrorAttributes(HttpServletRequest request,Exception ex){
		request.setAttribute(ERROR_INTERNAL_ATTRIBUTE, ex);}@OverridepublicMap<String,Object>getErrorAttributes(WebRequest webRequest,ErrorAttributeOptions options){Map<String,Object> errorAttributes=getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));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.remove("message");}if(!options.isIncluded(Include.BINDING_ERRORS)){
			errorAttributes.remove("errors");}return errorAttributes;}privateMap<String,Object>getErrorAttributes(WebRequest webRequest,boolean includeStackTrace){Map<String,Object> errorAttributes=newLinkedHashMap<>();
		errorAttributes.put("timestamp",newDate());addStatus(errorAttributes, webRequest);addErrorDetails(errorAttributes, webRequest, includeStackTrace);addPath(errorAttributes, webRequest);return errorAttributes;}privatevoidaddStatus(Map<String,Object> errorAttributes,RequestAttributes requestAttributes){Integer status=getAttribute(requestAttributes,RequestDispatcher.ERROR_STATUS_CODE);if(status==null){
			errorAttributes.put("status",999);
			errorAttributes.put("error","None");return;}
		errorAttributes.put("status", status);try{
			errorAttributes.put("error",HttpStatus.valueOf(status).getReasonPhrase());}catch(Exception ex){// Unable to obtain a reason
			errorAttributes.put("error","Http Status "+ status);}}privatevoidaddErrorDetails(Map<String,Object> errorAttributes,WebRequest webRequest,boolean includeStackTrace){Throwable error=getError(webRequest);if(error!=null){while(errorinstanceofServletException&& error.getCause()!=null){
				error= error.getCause();}
			errorAttributes.put("exception", error.getClass().getName());if(includeStackTrace){addStackTrace(errorAttributes, error);}}addErrorMessage(errorAttributes, webRequest, error);}privatevoidaddErrorMessage(Map<String,Object> errorAttributes,WebRequest webRequest,Throwable error){BindingResult result=extractBindingResult(error);if(result==null){addExceptionErrorMessage(errorAttributes, webRequest, error);}else{addBindingResultErrorMessage(errorAttributes, result);}}privatevoidaddExceptionErrorMessage(Map<String,Object> errorAttributes,WebRequest webRequest,Throwable error){
		errorAttributes.put("message",getMessage(webRequest, error));}protectedStringgetMessage(WebRequest webRequest,Throwable error){Object message=getAttribute(webRequest,RequestDispatcher.ERROR_MESSAGE);if(!ObjectUtils.isEmpty(message)){return message.toString();}if(error!=null&&StringUtils.hasLength(error.getMessage())){return error.getMessage();}return"No message available";}privatevoidaddBindingResultErrorMessage(Map<String,Object> errorAttributes,BindingResult result){
		errorAttributes.put("message","Validation failed for object='"+ result.getObjectName()+"'. "+"Error count: "+ result.getErrorCount());
		errorAttributes.put("errors", result.getAllErrors());}privateBindingResultextractBindingResult(Throwable error){if(errorinstanceofBindingResult){return(BindingResult) error;}returnnull;}privatevoidaddStackTrace(Map<String,Object> errorAttributes,Throwable error){StringWriter stackTrace=newStringWriter();
		error.printStackTrace(newPrintWriter(stackTrace));
		stackTrace.flush();
		errorAttributes.put("trace", stackTrace.toString());}privatevoidaddPath(Map<String,Object> errorAttributes,RequestAttributes requestAttributes){String path=getAttribute(requestAttributes,RequestDispatcher.ERROR_REQUEST_URI);if(path!=null){
			errorAttributes.put("path", path);}}@OverridepublicThrowablegetError(WebRequest webRequest){Throwable exception=getAttribute(webRequest, ERROR_INTERNAL_ATTRIBUTE);if(exception==null){
			exception=getAttribute(webRequest,RequestDispatcher.ERROR_EXCEPTION);}// store the exception in a well-known attribute to make it available to metrics// instrumentation.
		webRequest.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, exception,WebRequest.SCOPE_REQUEST);return exception;}@SuppressWarnings("unchecked")private<T>TgetAttribute(RequestAttributes requestAttributes,String name){return(T) requestAttributes.getAttribute(name,RequestAttributes.SCOPE_REQUEST);}}

BasicErrorController

ErrorMvcAutoConfiguration往容器中放了一个BasicErrorController类的组件,组件id为basicErrorController。

@Bean@ConditionalOnMissingBean(value=ErrorController.class, search=SearchStrategy.CURRENT)publicBasicErrorControllerbasicErrorController(ErrorAttributes errorAttributes,ObjectProvider<ErrorViewResolver> errorViewResolvers){returnnewBasicErrorController(errorAttributes,this.serverProperties.getError(),
			errorViewResolvers.orderedStream().collect(Collectors.toList()));}

BasicErrorController的定义如下。

@Controller@RequestMapping("${server.error.path:${error.path:/error}}")publicclassBasicErrorControllerextendsAbstractErrorController{//...@RequestMapping(produces=MediaType.TEXT_HTML_VALUE)publicModelAndViewerrorHtml(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:newModelAndView("error", model);}@RequestMappingpublicResponseEntity<Map<String,Object>>error(HttpServletRequest request){HttpStatus status=getStatus(request);if(status==HttpStatus.NO_CONTENT){returnnewResponseEntity<>(status);}Map<String,Object> body=getErrorAttributes(request,getErrorAttributeOptions(request,MediaType.ALL));returnnewResponseEntity<>(body, status);}}

@RequestMapping("${server.error.path:${error.path:/error}}"),控制器BasicErrorController可以处理的请求映射是:server.error.patherror.path/error,默认是/error
如果是浏览器客户端,则调用BasicErrorController#errorHTML方法进行处理,返回一个HTML格式的错误视图;如果是机器客户端,则调用BasicErrorController#error方法进行处理,返回一个JSON数据。

WhitelabelErrorViewConfiguration

@Configuration(proxyBeanMethods=false)@ConditionalOnProperty(prefix="server.error.whitelabel", name="enabled", matchIfMissing=true)@Conditional(ErrorTemplateMissingCondition.class)protectedstaticclassWhitelabelErrorViewConfiguration{privatefinalStaticView defaultErrorView=newStaticView();@Bean(name="error")@ConditionalOnMissingBean(name="error")publicViewdefaultErrorView(){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@ConditionalOnMissingBeanpublicBeanNameViewResolverbeanNameViewResolver(){BeanNameViewResolver resolver=newBeanNameViewResolver();
		resolver.setOrder(Ordered.LOWEST_PRECEDENCE-10);return resolver;}}

往容器中放入了一个View类的组件,组件id为error。(默认错误视图
往容器中放入了一个BeanNameViewResolver类的组件,组件id为beanNameViewResolver,它可以根据组件id解析视图。(用于解析 默认错误视图的视图解析器

默认错误视图是StaticView类,StaticView定义如下。
render方法中就是“whitelabel”错误视图的渲染过程。

privatestaticclassStaticViewimplementsView{//...@Overridepublicvoidrender(Map<String,?> model,HttpServletRequest request,HttpServletResponse response)throwsException{if(response.isCommitted()){String message=getMessage(model);
			logger.error(message);return;}
		response.setContentType(TEXT_HTML_UTF8.toString());StringBuilder builder=newStringBuilder();Object timestamp= model.get("timestamp");Object message= model.get("message");Object trace= model.get("trace");if(response.getContentType()==null){
			response.setContentType(getContentType());}
		builder.append("<html><body><h1>Whitelabel Error Page</h1>").append("<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>").append("<div id='created'>").append(timestamp).append("</div>").append("<div>There was an unexpected error (type=").append(htmlEscape(model.get("error"))).append(", status=").append(htmlEscape(model.get("status"))).append(").</div>");if(message!=null){
			builder.append("<div>").append(htmlEscape(message)).append("</div>");}if(trace!=null){
			builder.append("<div style='white-space:pre-wrap;'>").append(htmlEscape(trace)).append("</div>");}
		builder.append("</body></html>");
		response.getWriter().append(builder.toString());}//...}

DefaultErrorViewResolverConfiguration

@Configuration(proxyBeanMethods=false)@EnableConfigurationProperties({WebProperties.class,WebMvcProperties.class})staticclassDefaultErrorViewResolverConfiguration{privatefinalApplicationContext applicationContext;privatefinalResources resources;DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext,WebProperties webProperties){this<
  • 作者:richest_qi
  • 原文链接:https://blog.csdn.net/qzw752890913/article/details/122568678
    更新时间:2022-08-21 10:57:39