Spring MVC核心功能异常处理机制原理详解

如果在请求映射期间发生异常或从请求处理程序(例如@Controller)抛出异常,DispatcherServlet将委托给HandlerExceptionResolver bean链来解决异常并提供替代处理,这通常是一个错误响应 。概述如果在请求映射期间发生异常或从请求处理程序(例如@Controller)抛出异常,DispatcherServlet将委托给HandlerExceptionResolver
下表列出了可用的HandlerExceptionResolver实现:
HandlerExceptionResolver
描述
SimpleMAppingExceptionResolver
异常类名和错误视图名之间的映射 。用于在浏览器应用程序中渲染错误页面 。
DefaultHandlerExceptionResolver
解析Spring MVC引发的异常,并将其映射为HTTP状态码 。另见可选的ResponseEntityExceptionHandler和REST API异常 。
ResponseStatusExceptionResolver
使用@ResponseStatus注解解析异常,并根据注解中的值将异常映射为HTTP状态码 。
ExceptionHandlerExceptionResolver
通过在@Controller或@ControllerAdvice类中调用@ExceptionHandler方法来解决异常 。
异常解析器链你可以在Spring配置中声明多个HandlerExceptionResolver
HandlerExceptionResolver的约定规定它可以返回:

  • 指向错误视图的ModelAndView
  • 如果异常是在解析器中处理的,则返回空的ModelAndView
  • 如果异常仍然未解决,则为null,以便后续的解析器尝试,如果异常在最后仍然存在,则允许它向上冒泡到Servlet容器 。
MVC配置自动为默认的Spring MVC异常、@ResponseStatus注解的异常以及@ExceptionHandler方法声明了内置的解析器 。
容器错误页配置如果任何HandlerExceptionResolver都无法解决异常,因此需要传播,或者响应状态被设置为错误状态(即4xx、5xx),Servlet容器可以在html中渲染一个默认的错误页面 。要定制容器的默认错误页面,可以在web.xml中声明错误页面映射 。如下面的例子所示:?
 <error-page><location>/error</location></error-page>根据前面的示例,当出现异常冒泡或响应具有错误状态时,Servlet容器将在容器中向配置的URL(例如/error)发送错误 。然后由DispatcherServlet处理,可能会将其映射到一个@Controller,这个@Controller可以实现为返回一个带有模型的错误视图名称,或者渲染一个JSON响应,如下面的例子所示:?
 @RestControllerpublic class ErrorController {@RequestMapping(path = "/error")public Map<String, Object> handle(HttpServletRequest request) {Map<String, Object> map = new HashMap<String, Object>();map.put("status", request.getAttribute("JAVAx.servlet.error.status_code"));map.put("reason", request.getAttribute("javax.servlet.error.message"));return map;}}错误处理原理 public class DispatcherServlet {// 取得容器中所有的异常解析器private List<HandlerExceptionResolver> handlerExceptionResolvers;protected void initStrategies(ApplicationContext context) {// 初始化异常解析器initHandlerExceptionResolvers(context);}private void initHandlerExceptionResolvers(ApplicationContext context) {this.handlerExceptionResolvers = null;if (this.detectAllHandlerExceptionResolvers) {// 获取容器中所有的异常解析器Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);}}}} 【Spring MVC核心功能异常处理机制原理详解】发生异常后逻辑处理?
public class DispatcherServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;// 判断当前调用是否发生了异常if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {mv = ((ModelAndViewDefiningException) exception).getModelAndView();} else {// 获取当前处理句柄HandlerMethodObject handler = (mappedHandler != null ? mappedHandler.getHandler() : null);// 处理异常mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}if (mv != null && !mv.wasCleared()) {render(mv, request, response);}}protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);ModelAndView exMv = null;if (this.handlerExceptionResolvers != null) {// 遍历在上面初始化查找到的所有异常解析器for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {// 执行异常解析exMv = resolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {// ...return exMv ;}// 如果所有的异常解析器都返回null,则直接抛出异常,该异常会被Servlet进行处理throw ex ;}}


推荐阅读