ResponseStatusExceptionResolver
若抛出的异常类型上有@ResponseStatus
注解,那么此处理器就会处理,并且状态码会返给response。Spring5.0
还能处理ResponseStatusException
这个异常(此异常是5.0新增)
// 实现了接口MessageSourceAware,方便拿到国际化资源,方便错误消息的国际化 // @since 3.0 public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware { @Nullable private MessageSource messageSource; @Override public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } @Override @Nullable protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { try { // 若异常类型是,那就处理这个异常 // 处理很简单:response.sendError(statusCode, resolvedReason) // 当然会有国际化消息的处理。最终new一个空的new ModelAndView()供以返回 if (ex instanceof ResponseStatusException) { return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler); } // 若异常类型所在的类上标注了ResponseStatus注解,就处理这个状态码 //(可见:异常类型优先于ResponseStatus) // 处理方式同上~~~~ ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class); if (status != null) { return resolveResponseStatus(status, request, response, handler, ex); } // 这里有个递归:如果异常类型是Course里面的,也会继续处理,所以需要注意这里的递归处理 if (ex.getCause() instanceof Exception) { return doResolveException(request, response, handler, (Exception) ex.getCause()); } } catch (Exception resolveEx) { // 处理失败,就记录warn日志(非info哦~) if (logger.isWarnEnabled()) { logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx); } } return null; } }
这里有个处理的小细节:递归调用了doResolveException()方法,也就是说若有coouse原因也是异常,那就继续会尝试处理的。
另外请注意:@ResponseStatus标注在异常类上此处理器才会处理,而不是标注在处理方法上,或者所在类上哦,所以一般用于自定义异常时使用。
DefaultHandlerExceptionResolver
默认的异常处理器。它能够处理标准的Spring MVC异常们,并且把它转换为对应的HTTP status codes,一般作为兜底处理,Spring MVC默认也注册了此处理器。它能处理的异常非常之多,简单列出来如下:
// @since 3.0 public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver { public DefaultHandlerExceptionResolver() { setOrder(Ordered.LOWEST_PRECEDENCE); setWarnLogCategory(getClass().getName()); // 不同的日志采用不同的记录器是个很好的习惯 } @Override @Nullable protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { try { if (ex instanceof HttpRequestMethodNotSupportedException) { return handleHttpRequestMethodNotSupported( (HttpRequestMethodNotSupportedException) ex, request, response, handler); } else if (ex instanceof HttpMediaTypeNotSupportedException) { return handleHttpMediaTypeNotSupported( (HttpMediaTypeNotSupportedException) ex, request, response, handler); } ... // 省略其它的else if // 多有的handle方法几乎一样的,都是response.sendError() // 有的还会esponse.setHeader("Accept", MediaType.toString(mediaTypes));等等 } }
它对这些异常的处理,亦可参考内置的ResponseEntityExceptionHandler实现,它提供了基于@ExceptionHandler的很多异常类型的处理。
DispatcherServlet对它的初始化和应用
因为Spring MVC对请求的整个处理流程都是由DispatcherServlet来控制的,异常处理也属于请求的一部分,所以它的初始化和应用都在此处。
初始化
虽然异常处理非常重要,但绝大多数情况下你可能并不知道Spring MVC它内置就自动给我们配置好了一些异常处理器。DispatcherServlet初始化它的相关代码如下:
DispatcherServlet: protected void initStrategies(ApplicationContext context) { ... initHandlerExceptionResolvers(context); // 第六步 ... } // 寻找逻辑(detectAllHandlerExceptionResolvers默认值是true表示回去容器里寻找): // 1、若detect = true(默认是true),去容器里找出所有`HandlerExceptionResolver`类型的Bean们,找到后排序 // 2、若detect = false(可手动更改),那就拿名称为`handlerExceptionResolver`这单独的一个Bean(context.getBean()) // 3、如果一个都木有找到,那就走默认策略getDefaultStrategies(),详见下面截图~~~ private void initHandlerExceptionResolvers(ApplicationContext context) { this.handlerExceptionResolvers = null; if (this.detectAllHandlerExceptionResolvers) { // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts. Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values()); // We keep HandlerExceptionResolvers in sorted order. AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers); } } else { try { HandlerExceptionResolver her = context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class); this.handlerExceptionResolvers = Collections.singletonList(her); } catch (NoSuchBeanDefinitionException ex) { // Ignore, no HandlerExceptionResolver is fine too. } } // Ensure we have at least some HandlerExceptionResolvers, by registering // default HandlerExceptionResolvers if no other resolvers are found. if (this.handlerExceptionResolvers == null) { this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
参照此初始化逻辑,可得出如下结论(缺省情况下):
- 开启@EnableWebMvc后,使用的异常处理器是HandlerExceptionResolverComposite。截图如下:
- 若不开启@EnableWebMvc,就执行默认策略,装配如下处理器
应用流程
请求交给Handler
处理后得到返回结果Result
,但result可能会有异常,因此DispatcherServlet
会针对性对result
做处理:
DispatcherServlet: // 处理request请求 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); ... // 全部处理完成后,这中间可以是真正结果,也有可能有异常,交给结果处理器 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); ... // 执行拦截器的AfterCompletion方法 } private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; // 不等于null,说明有异常哦~~~~ 那就处理异常 if (exception != null) { // 此种异常属于Spring MVC内部的异常 if (exception instanceof ModelAndViewDefiningException) { mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { // 若是普通异常,就交给方法processHandlerException()去统一处理 // 从而得到一个异常视图ModelAndView,并且标注errorView = true(若不为null的话) Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } ... // 渲染此错误视图(若不为null) render(mv, request, response) ... } protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { ... ModelAndView exMv = null; // 核心处理办法就在此处,exMv 只有有一个视图返回了,就立马停止(短路效果) if (this.handlerExceptionResolvers != null) { for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) { exMv = resolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } } ... // 后面处理viewName等等~~~~~~~ }
从应用流程上看是比较简单的,但是了解了此处理流程对我们后续使用、定制会有很好的促进作用。