SpringMVC异常友好界面

软件测试可以帮助我们发现bug,减少bug,但不能做到十全十美,没有一个bug,所以就需要在用户遇到这些未知bug时我们能够提供友好的界面显示错误,让用户知道错误发生的原因是自己操作问题还是系统的不完美的同时不至于直接打印出错误的详细内容。

下面添加一段代码,简要说明SpringMVC执行流程:

DispatcherServlet继承于HttpServlet(这里是指DispatcherServlet是Httpservlet的后代HttpServletBean的后代FrameworkServlet的后代);经过多层封装,所有的请求不管是post还是get之类都会执行doDispatch方法。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  HttpServletRequest processedRequest = request;
  HandlerExecutionChain mappedHandler = null;
  boolean multipartRequestParsed = false;
  WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  try {
   ModelAndView mv = null;
   Exception dispatchException = null;
   try {
    processedRequest = checkMultipart(request);
    multipartRequestParsed = processedRequest != request;
    // Determine handler for the current request. 根据请求获取需要执行的handle 
    mappedHandler = getHandler(processedRequest, false);
    if (mappedHandler == null || mappedHandler.getHandler() == null) {
     //没有找到即没有相应的handle 即404
     noHandlerFound(processedRequest, response);
     return;
    }
    // Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    // Process last-modified header, if supported by the handler.
    String method = request.getMethod();
    boolean isGet = "GET".equals(method);
    //处理GET/HEAD且支持last modified的请求
    if (isGet || "HEAD".equals(method)) {
     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
     if (logger.isDebugEnabled()) {
      String requestUri = urlPathHelper.getRequestUri(request);
      logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
     }
     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
      return;
     }
    }
    //执行拦截器的 prehandle ,如果返回false或者抛出异常则不会执行 post 
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
     return;
    }
    try {
     // Actually invoke the handler. 调用实际方法
     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    }
    finally {
     if (asyncManager.isConcurrentHandlingStarted()) {
      return;
     }
    }
    //设置默认视图:如果返回空视图,且配置有默认视图
    applyDefaultViewName(request, mv);
    //执行拦截器post
    mappedHandler.applyPostHandle(processedRequest, response, mv);
   }
   catch (Exception ex) {
    dispatchException = ex;
   }
//处理结果,渲染,和执行拦截器after
   processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  }
  catch (Exception ex) {
   triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  }
  catch (Error err) {
   triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
  }
  finally {
   if (asyncManager.isConcurrentHandlingStarted()) {
    // Instead of postHandle and afterCompletion
    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    return;
   }
   // Clean up any resources used by a multipart request.
   if (multipartRequestParsed) {
    cleanupMultipart(processedRequest);
   }
  }
 }

/**
  * Handle the result of handler selection and handler invocation, which is
  * either a ModelAndView or an Exception to be resolved to a ModelAndView.
  */
 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
   HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

  boolean errorView = false;

  //处理异常:在执行preHandle和handle过程报的错
  if (exception != null) {
   //处理视图定义异常
   if (exception instanceof ModelAndViewDefiningException) {
    logger.debug("ModelAndViewDefiningException encountered", exception);
    mv = ((ModelAndViewDefiningException) exception).getModelAndView();
   }
   else {
    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
    mv = processHandlerException(request, response, handler, exception);
    errorView = (mv != null);
   }
  }

  //渲染视图
  // Did the handler return a view to render?
  if (mv != null && !mv.wasCleared()) {
   render(mv, request, response);
   if (errorView) {
    WebUtils.clearErrorRequestAttributes(request);
   }
  }
  else {
   if (logger.isDebugEnabled()) {
    logger.debug("Null ModelAndView returned to DispatcherServlet with name ‘" + getServletName() +
      "‘: assuming HandlerAdapter completed request handling");
   }
  }

  if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
   // Concurrent handling started during a forward
   return;
  }

  if (mappedHandler != null) {
   mappedHandler.triggerAfterCompletion(request, response, null);
  }
 }

/**
  * Determine an error ModelAndView via the registered HandlerExceptionResolvers.
  * @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 ModelAndView to forward to
  * @throws Exception if no error ModelAndView found
  */
 protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
   Object handler, Exception ex) throws Exception {

  // Check registered HandlerExceptionResolvers...
  ModelAndView exMv = null;
  //通过配置获取异常友好异常
  for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
   exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
   if (exMv != null) {
    break;
   }
  }
  if (exMv != null) {
   if (exMv.isEmpty()) {
    return null;
   }
   // We might still need view name translation for a plain error model...
   if (!exMv.hasView()) {
    exMv.setViewName(getDefaultViewName(request));
   }
   if (logger.isDebugEnabled()) {
    logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
   }
   WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
   return exMv;
  }

  throw ex;
 }

 

 从上面代码中可以看出最后执行HandlerExceptionResolver中的resolveException函数来获取异常视图,

第一种:使用注解:(默认应该是:AnnotationMethodHandlerExceptionResolver)

 添加父类,父类包含一个函数

 @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception e) {
        return new ModelAndView(new RedirectView("error"), "errClass", ClassUtils.getShortName(e.getClass()));
 }

 

其他注有@Controller的类都继承于该类

第二种:自定义类,实现HandlerExceptionResolver接口;

第三种:使用SpringMVC定义好的类SimpleMappingExceptionResolver

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。