SpringBoot错误处理的自动配置源码学习笔记

错误处理的自动配置

	@Configuration
	@ConditionalOnWebApplication(type = Type.SERVLET)
	@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
	// Load before the main WebMvcAutoConfiguration so that the error View is available
	@AutoConfigureBefore(WebMvcAutoConfiguration.class)
	@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class, WebMvcProperties.class })
	
	public class ErrorMvcAutoConfiguration {
		
		@Bean
		//容器中没有这个组件的时候
		@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
		public DefaultErrorAttributes errorAttributes() {
			return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
		}
		
		//处理/error请求的
		@Bean
		@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
		public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
			return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
		}
		
		//错误的定制页面,定制错误的响应规则
		@Bean
		public ErrorPageCustomizer errorPageCustomizer() {
			return new ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath);
		}
		
		@Configuration
		static class DefaultErrorViewResolverConfiguration {
			//帮我们在页面共享信息,
			@Bean
			@ConditionalOnBean(DispatcherServlet.class)
			@ConditionalOnMissingBean(ErrorViewResolver.class)
			public DefaultErrorViewResolver conventionErrorViewResolver() {
				return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
			}
		}
		
		//注册错误页面的响应规则
		private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
			@Override
			public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
				ErrorPage errorPage = new ErrorPage(
						this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
				errorPageRegistry.addErrorPages(errorPage);
			}
		}
		@Configuration
		@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true)
		@Conditional(ErrorTemplateMissingCondition.class)
		protected static class WhitelabelErrorViewConfiguration {
			
			private final StaticView defaultErrorView = new StaticView();

			//视图对象就叫做error
			@Bean(name = "error")
			@ConditionalOnMissingBean(name = "error")
			public View defaultErrorView() {
				return this.defaultErrorView;
			}
		}
		private static class StaticView implements View {
			@Override
			public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
				
				private static final Log logger = LogFactory.getLog(StaticView.class);

				@Override
				public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
						throws Exception {
					if (response.isCommitted()) {
						String message = getMessage(model);
						logger.error(message);
						return;
					}
					StringBuilder builder = new StringBuilder();
					Date timestamp = (Date) 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());
			}
		}
	}

getPath()

		源码:
		//从配置文件中取出error.path的值
		public class ErrorProperties {
			@Value("${error.path:/error}")
			private String path = "/error";
		}

BasicErrorController//处理/error请求的

		源码:
		//处理/error请求的,error请求是自适应的
		@Controller
		@RequestMapping("${server.error.path:${error.path:/error}}")
		public class BasicErrorController extends AbstractErrorController {
			//产生html类型的数据,浏览器发送的请求来到这个方法处理
			@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, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
				//响应帮他设置状态码
				response.setStatus(status.value());
				//解析异常视图,去哪个页面作为错误页面:包含页面地址和页面内容
				ModelAndView modelAndView = resolveErrorView(request, response, status, model);
				return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
			}
			//产生json数据,其他客户端这个方法处理
			@RequestMapping
			public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
				HttpStatus status = getStatus(request);
				if (status == HttpStatus.NO_CONTENT) {
					return new ResponseEntity<Map<String, Object>>(status);
				}
				Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
				return new ResponseEntity<>(body, status);
			}
		}

TEXT_HTML_VALUE

			源码:
			public static final String TEXT_HTML_VALUE = "text/html";

getStatus

			源码:
			public abstract class AbstractErrorController implements ErrorController {
				protected HttpStatus getStatus(HttpServletRequest request) {
					Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
					if (statusCode == null) {
						return HttpStatus.INTERNAL_SERVER_ERROR;
					}
					try {
						return HttpStatus.valueOf(statusCode);
					}
					catch (Exception ex) {
						return HttpStatus.INTERNAL_SERVER_ERROR;
					}
				}
			}

getErrorAttributes

			源码:
			protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
				WebRequest webRequest = new ServletWebRequest(request);
				return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
			}

errorAttributes

				源码:
				public interface ErrorAttributes {
				}

ErrorAttributes

					实现:
					@Order(Ordered.HIGHEST_PRECEDENCE)
					public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {
						//页面能获取的信息
						@Override
						public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
							Map<String, Object> errorAttributes = new LinkedHashMap<>();
							//timestamp时间戳
							errorAttributes.put("timestamp", new Date());
							//status添加状态码
							addStatus(errorAttributes, webRequest);
							addErrorDetails(errorAttributes, webRequest, includeStackTrace);
							addPath(errorAttributes, webRequest);
							return errorAttributes;
						}
						private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
							Integer status = getAttribute(requestAttributes, "javax.servlet.error.status_code");
							if (status == null) {
								errorAttributes.put("status", 999);
								//error错误的提示
								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);
							}
						}
						private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest,
							boolean includeStackTrace) {
							Throwable error = getError(webRequest);
							if (error != null) {
								while (error instanceof ServletException && error.getCause() != null) {
									error = error.getCause();
								}
								if (this.includeException) {
									//exception异常对象
									errorAttributes.put("exception", error.getClass().getName());
								}
								addErrorMessage(errorAttributes, error);
								if (includeStackTrace) {
									addStackTrace(errorAttributes, error);
								}
							}
							Object message = getAttribute(webRequest, "javax.servlet.error.message");
							if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null)
									&& !(error instanceof BindingResult)) {
								//message异常消息
								errorAttributes.put("message", StringUtils.isEmpty(message) ? "No message available" : message);
							}
						}
						private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
							BindingResult result = extractBindingResult(error);
							if (result == null) {
								errorAttributes.put("message", error.getMessage());
								return;
							}
							if (result.hasErrors()) {
							//JSR303数据校验的错误
								errorAttributes.put("errors", result.getAllErrors());
								errorAttributes.put("message", "Validation failed for object='" + result.getObjectName()
										+ "'. Error count: " + result.getErrorCount());
							}
							else {
								errorAttributes.put("message", "No errors");
							}
						}
					}

resolveErrorView

		源码:
		public abstract class AbstractErrorController implements ErrorController {
			protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,
					Map<String, Object> model) {
				//拿到所有异常视图解析器
				for (ErrorViewResolver resolver : this.errorViewResolvers) {
					//得到ModelAndView就返回,得不到就null
					ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
					if (modelAndView != null) {
						return modelAndView;
					}
				}
				return null;
			}
		}

DefaultErrorViewResolver//帮我们在页面共享信息

		源码:
		public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
			static {
				Map<Series, String> views = new EnumMap<>(Series.class);
				//客户端错误用4xx
				views.put(Series.CLIENT_ERROR, "4xx");
				//服务端错误用5xx
				views.put(Series.SERVER_ERROR, "5xx");
				SERIES_VIEWS = Collections.unmodifiableMap(views);
			}
			@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())) {
					//status状态码
					modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
				}
				return modelAndView;
			}
			private ModelAndView resolve(String viewName, Map<String, Object> model) {
				//默认SpringBoot可以去找到一个页面,error/404
				String errorViewName = "error/" + viewName;
				//模板引擎可以解析这个页面地址就用模板引擎解析
				TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
						this.applicationContext);
				if (provider != null) {
					//模板引擎可用的情况下放回到errorViewName指定的视图地址
					return new ModelAndView(errorViewName, model);
				}
				//模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面,error/404.html
				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");
						//如果静态文件夹下存在HtmlResourceView
						if (resource.exists()) {
							return new ModelAndView(new HtmlResourceView(resource), model);
						}
					}
					catch (Exception ex) {
					}
				}
				return null;
			}		
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值