欢迎光临
我们一直在努力

SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver

springmvc支持服务端在处理业务逻辑过程中出现异常的时候可以配置相应的modelandview对象返回给客户端,本文介绍springmvc默认的几种handlerExceptionResolver类

springmvc的xml配置化-Exception配置

<bean id="exceptionHandler" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!--设置默认返回viewName,通常与freemarker引擎搭配使用--> <property name="defaultErrorView" value="error/defaultError" /> <!--设置默认返回response status--> <property name="defaultStatusCode" value="500" /> <!--配置相应的异常类与viewName的映射--> <property name="exceptionMappings"> <props> <prop key="SessionTimeoutException">redirect:../login.html</prop> <prop key="AuthenticationException">error/403</prop> </props> </property> </bean> 

以上的配置会对出SessionTimeoutException异常则跳转至login页面,对AuthenticationException异常则跳转至403页面,对其他的异常则默认跳转至defaultError页面呈现并返回500的错误码

接口内只有一个方法resolveException(),通过解析异常查询配置以得到符合条件的ModelAndView对象

 /** * Try to resolve the given exception that got thrown during handler execution, * returning a {@link ModelAndView} that represents a specific error page if appropriate. * <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty} * to indicate that the exception has been resolved successfully but that no view * should be rendered, for instance by setting a status code. * @param request current HTTP request * @param response current HTTP response * @param handler the executed handler, or {@code null} if none chosen at the * time of the exception (for example, if multipart resolution failed) * @param ex the exception that got thrown during handler execution * @return a corresponding {@code ModelAndView} to forward to, or {@code null} * for default processing */ ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex); 

所有的spring内置异常解析类都继承于此,直奔主题看resolveException()方法

 @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //判断是否需要解析 if (shouldApplyTo(request, handler)) { //此处一般是判断内部属性preventResponseCaching是否为true,是则设置响应包头cache-control:no-store prepareResponse(ex, response); //使用模板方法doResolveException()方法供子类实现 ModelAndView result = doResolveException(request, response, handler, ex); if (result != null) { //日志打印一发 logException(ex, request); } return result; } else { return null; } } 

附带着分析下shouldApplyTo()方法

 /** **可以配置mappedHandlers和mappedHandlerClasses属性来特定匹配 **默认情况下两者都为空则直接返回true,表明对所有的handler都进行异常解析 */ protected boolean shouldApplyTo(HttpServletRequest request, Object handler) { //此处的handler一般为bean对象 if (handler != null) { if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) { return true; } if (this.mappedHandlerClasses != null) { for (Class<?> handlerClass : this.mappedHandlerClasses) { if (handlerClass.isInstance(handler)) { return true; } } } } // Else only apply if there are no explicit handler mappings. return (this.mappedHandlers == null && this.mappedHandlerClasses == null); } 

比较简单的实现类,可以配绑定viewName和exception以完成简单的异常映射视图页面

 protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // Expose ModelAndView for chosen error view. String viewName = determineViewName(ex, request); if (viewName != null) { //如果配置了statusCodes属性,则对此异常的状态码进行设置 Integer statusCode = determineStatusCode(request, viewName); if (statusCode != null) { applyStatusCodeIfPossible(request, response, statusCode); } //创建ModelAndView对象 return getModelAndView(viewName, ex, request); } else { return null; } } 

针对以上的源码我们分两步去简单分析下

 protected String determineViewName(Exception ex, HttpServletRequest request) { String viewName = null; //判断异常是否属于excludeExceptions集合内,是则直接返回null if (this.excludedExceptions != null) { for (Class<?> excludedEx : this.excludedExceptions) { if (excludedEx.equals(ex.getClass())) { return null; } } } // Check for specific exception mappings. // 从exceptionMappings集合内根据exception获取到相应的viewName if (this.exceptionMappings != null) { viewName = findMatchingViewName(this.exceptionMappings, ex); } //当exceptionMappings集合内不存在指定的exception但是默认视图指定则直接返回默认视图 if (viewName == null && this.defaultErrorView != null) { viewName = this.defaultErrorView; } return viewName; } 
  • excludedExceptions集合可以过滤指定的exception,对其不进行解析直接返回null。可配置

  • exceptionMappings绑定了exception与viewName的关系,如果在其集合内没找到相应的viewName,但是defaultErrorView属性指定,则会直接返回defaultErrorView对应的视图

 protected ModelAndView getModelAndView(String viewName, Exception ex) { ModelAndView mv = new ModelAndView(viewName); //exceptionAttribute默认为exception if (this.exceptionAttribute != null) { //将exception信息添加到model中 mv.addObject(this.exceptionAttribute, ex); } return mv; } 

主要就是将exceptionAttribute对应的参数值默认为exception属性添加到视图对象的model中

主要是解析带有@ResponseStatus的异常类,将其中的异常信息描述直接返回给客户端

 @Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //获取相应类上的注解@ResponseStatus ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class); if (responseStatus != null) { try { return resolveResponseStatus(responseStatus, request, response, handler, ex); } catch (Exception resolveEx) { logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx); } } else if (ex.getCause() instanceof Exception) { ex = (Exception) ex.getCause(); //递归 return doResolveException(request, response, handler, ex); } return null; } 

读取@ResponseStatus注解信息,返回异常内容给客户端

 protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { //状态码 int statusCode = responseStatus.code().value(); //异常原因描述 String reason = responseStatus.reason(); if (this.messageSource != null) { reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()); } //通过response对象直接返回错误信息给客户端 if (!StringUtils.hasLength(reason)) { response.sendError(statusCode); } else { //通过response对象直接返回错误信息给客户端 response.sendError(statusCode, reason); } return new ModelAndView(); } 

源码就不公布了,读者可自行去查询,基本都是调用response的sendError()方法返回错误信息给客户端。本文对其中的异常归下类

  1. 请求方式异常
  • HttpRequestMethodNotSupportedException-服务端不支持相应的请求方法
  • HttpMediaTypeNotSupportedException/HttpMediaTypeNotAcceptableException-服务端/客户端不支持相应的mediaType,比如application/json
  • MissingPathVariableException-@PathVaribale指定参数请求中不包含
  • MissingServletRequestParameterException/ServletRequestBindingException-请求参数绑定错误
  • MethodArgumentNotValidException-@Valid注解指定的参数校验失败
  • AsyncRequestTimeoutException-异步请求超时
  1. 消息内容异常
  • ConversionNotSupportedException-服务端找寻不到相应的Convert对象来解析javabean
  • TypeMismatchException-设置javabean属性类型出错
  • HttpMessageNotReadableException/HttpMessageNotWritableException-消息内容不可读/不可写
  • MissingServletRequestPartException-文件上传类错误,可能请求没有multipart/form-data或者服务不支持文件上传
  • NoHandlerFoundException-handler处理器没有找到,即可能没有对应的请求处理供响应

具体的逻辑本文则不展开了,简述下其中的逻辑:
当处理handlerMethod业务逻辑过程中出现了异常,则此解析器

  1. 尝试从handlerMethod所在的class类去找寻是否含有@ExceptionHandler注解的方法

  2. 判断@ExceptionHandler指定的exception类与产生的异常一致,一致则执行相应的方法,当有多个@ExceptionHandler(value),则默认采用第一个

  3. 当上述在class类找寻不到则尝试判断class类是否含有@ControllerAdvice注解,有则按照上述第一二步的步骤再次找寻@ControllerAdvice指定的类

springmvc开放了对异常也可以包装成页面显示的功能,通过本文的简单分析可以帮助博主和读者更好的理解springmvc对异常的处理

  • 海报
海报图正在生成中...
赞(0) 打赏
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
文章名称:《SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver》
文章链接:https://www.456zj.com/20393.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址