SpringMVC源码分析系列(5)-HandlerExceptionResolver

354 阅读3分钟

HandlerExceptionResolver接口定义

定义: 根据异常类型匹配处理过程来对异常进行处理

接口定义

public interface HandlerExceptionResolver {
   ModelAndView resolveException(
         HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}

ExceptionHandlerExceptionResolver

继承关系

ExceptionHandlerExceptionResolver

初始化过程

同样先来看InitializingBean

@Override
public void afterPropertiesSet() {
   //初始化@ControllerAdvices
   initExceptionHandlerAdviceCache();
   //初始化默认ArgumentResolver
   if (this.argumentResolvers == null) {
      List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
      this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
   }
  //初始化默认ReturnValueHandler
   if (this.returnValueHandlers == null) {
      List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
      this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
   }
}
private void initExceptionHandlerAdviceCache() {
  //获取所有@ControllerAdvices注解的类
  List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
  //进行排序
  AnnotationAwareOrderComparator.sort(adviceBeans);

  for (ControllerAdviceBean adviceBean : adviceBeans) {
    ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
    //将带有@Exceptionhanlder注解的方法添加到exceptionHandlerAdviceCache中
    if (resolver.hasExceptionMappings()) {
      this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
    }
    //如果该类同时实现ResponseBodyAdvice接口 ,则添加到responseBodyAdvice中
    if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
      this.responseBodyAdvice.add(adviceBean);
    }
  }
}

过程概括:(过程与RequestMappingHandlerAdapter十分相似)

  1. 初始化@ControllerAdvices注解的类
    1. 获取所有@ControllerAdvices注解的类
    2. 将带有@Exceptionhanlder注解的方法添加到exceptionHandlerAdviceCache中
    3. 如果该类同时实现ResponseBodyAdvice接口 ,则添加到responseBodyAdvice中
  2. 初始化默认ArgumentResolver
  3. 初始化默认ReturnValueHandler

初始化

DispatcherServlet调用ExceptionHandlerExceptionResolver过程

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
  boolean errorView = false;
  //如果发生异常
  if (exception != null) {
    //如果抛出的是一个处理过的异常
    if (exception instanceof ModelAndViewDefiningException) {
      //直接从异常中获取ModelAndView
      mv = ((ModelAndViewDefiningException) exception).getModelAndView();
    }
    else {
      //处理异常
      Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
      //真正执行异常处理的过程
      mv = processHandlerException(request, response, handler, exception);
      errorView = (mv != null);
    }
  }

  //视图解析过程
  ...
    //如果是异步处理方式,且处于处理中阶段 则直接返回
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
    }
  //触发HandlerExecutionChain中的Intercepter.afterCompletion()回调
  if (mappedHandler != null) {
    mappedHandler.triggerAfterCompletion(request, response, null);
  }
}

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse 	response,Object handler, Exception ex) throws Exception {
  // Check registered HandlerExceptionResolvers...
  ModelAndView exMv = null;
  //遍历handlerExceptionResolvers处理异常 处理成功就结束
  for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
    //调用 HandlerExceptionResolver.resolveException()方法处理异常
    exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
    if (exMv != null) {
      break;
    }
  }
  //异常处理成功
  if (exMv != null) {
    //异常处理没结果 则返回空
    if (exMv.isEmpty()) {
      request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
      return null;
    }
    //得到异常处理后视图 
    if (!exMv.hasView()) {
      exMv.setViewName(getDefaultViewName(request));
    }
    //不知道啥用
    WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
    return exMv;
  }
  //异常处理不成功抛出异常
  throw ex;
}

ExceptionHandlerExceptionResolver.resolveException()过程

#org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
      Object handler, Exception ex) {
   //判断是否支持该异常类型的处理
   if (shouldApplyTo(request, handler)) {
     ...
      ModelAndView result = doResolveException(request, response, handler, ex);
      return result;
   }
   else {
      return null;
   }
}

#org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver
@Override
protected final ModelAndView doResolveException(
  HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
  return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}

#org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
@Override
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
              HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
  //1.根据异常类型得到处理过程ServletInvocableHandlerMethod
  ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
  //不支持该类型异常的处理则返回
  if (exceptionHandlerMethod == null) {
    return null;
  }
  //提供参数解析的支持
  exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  //提供返回值转化的支持
  exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);

  ServletWebRequest webRequest = new ServletWebRequest(request, response);
  //处理过程上下文
  ModelAndViewContainer mavContainer = new ModelAndViewContainer();

  try {
    Throwable cause = exception.getCause();
    if (cause != null) {
      //2. 参数解析,过程调用
      exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
    }else {
      exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
    }
  }
  catch (Throwable invocationEx) {
 	//处理过程中如果发生异常,则视为不支持该异常类型的解析,返回null
    return null;
  }
  //TODO 不知道干嘛 
  if (mavContainer.isRequestHandled()) {
    return new ModelAndView();
  }
  else {
    ModelMap model = mavContainer.getModel();
    HttpStatus status = mavContainer.getStatus();
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
    mav.setViewName(mavContainer.getViewName());
    if (!mavContainer.isViewReference()) {
      mav.setView((View) mavContainer.getView());
    }
    if (model instanceof RedirectAttributes) {
      Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
      request = webRequest.getNativeRequest(HttpServletRequest.class);
      RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
    }
    return mav;
  }
}

过程总结:

  1. 根据异常类型得到处理过程ServletInvocableHandlerMethod

    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
      //HandlerMethod所属的类
      Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
      if (handlerMethod != null) {
        //从exceptionHandlerCache获取局部@ExceptionHandler的缓存
        ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
        if (resolver == null) {
          //从该类中得到@ExceptionHandler方法
          resolver = new ExceptionHandlerMethodResolver(handlerType);
          //存到exceptionHandlerCache
          this.exceptionHandlerCache.put(handlerType, resolver);
        }
        //使用局部@ExceptionHandler方法处理异常
        Method method = resolver.resolveMethod(exception);
        if (method != null) {
          return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
        }
      }
      //遍历全局@ExceptionHandler处理异常
      for (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
        if (entry.getKey().isApplicableToBeanType(handlerType)) {
          ExceptionHandlerMethodResolver resolver = entry.getValue();
          Method method = resolver.resolveMethod(exception);
          if (method != null) {
            return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
          }
        }
      }
      return null;
    }
    
  2. 使用ServletInvocableHandlerMethod进行参数解析,过程调用.(这里的过程和HandlerAdapter中一样,只是把ServletInvocableHandlerMethod中的handler方法换成了异常处理的handler方法)

注意:@ExceptionHandler的异常类型不能重复