private void initExceptionHandlerAdviceCache() {
    if (getApplicationContext() == null) {
      return;
    }
    if (logger.isDebugEnabled()) {
      logger.debug("Looking for exception mappings: " + getApplicationContext());
    }

    List<ControllerAdviceBean> adviceBeans =
        ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    AnnotationAwareOrderComparator.sort(adviceBeans);

    for (ControllerAdviceBean adviceBean : adviceBeans) {
      ExceptionHandlerMethodResolver resolver =
          new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
      if (resolver.hasExceptionMappings()) {
        this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
        if (logger.isInfoEnabled()) {
          logger.info("Detected @ExceptionHandler methods in " + adviceBean);
        }
      }
      if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
        this.responseBodyAdvice.add(adviceBean);
        if (logger.isInfoEnabled()) {
          logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);
        }
      }
    }
  }
  /**
   * Find an {@code @ExceptionHandler} method for the given exception. The default implementation
   * searches methods in the class hierarchy of the controller first and if not found, it continues
   * searching for additional {@code @ExceptionHandler} methods assuming some {@linkplain
   * ControllerAdvice @ControllerAdvice} Spring-managed beans were detected.
   *
   * @param handlerMethod the method where the exception was raised (may be {@code null})
   * @param exception the raised exception
   * @return a method to handle the exception, or {@code null}
   */
  protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
      HandlerMethod handlerMethod, Exception exception) {
    Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);

    if (handlerMethod != null) {
      ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
      if (resolver == null) {
        resolver = new ExceptionHandlerMethodResolver(handlerType);
        this.exceptionHandlerCache.put(handlerType, resolver);
      }
      Method method = resolver.resolveMethod(exception);
      if (method != null) {
        return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
      }
    }

    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;
  }
 protected InvocableHandlerMethod findExceptionHandler(
     HandlerMethod handlerMethod, Exception exception) {
   if (handlerMethod == null) {
     return null;
   }
   Class<?> handlerType = handlerMethod.getBeanType();
   ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
   if (resolver == null) {
     resolver = new ExceptionHandlerMethodResolver(handlerType);
     this.exceptionHandlerCache.put(handlerType, resolver);
   }
   Method method = resolver.resolveMethod(exception);
   return (method != null ? new InvocableHandlerMethod(handlerMethod.getBean(), method) : null);
 }
 /**
  * Find the @{@link ExceptionHandler} method for the given exception. The default implementation
  * searches @{@link ExceptionHandler} methods in the class hierarchy of the method that raised the
  * exception.
  *
  * @param handlerMethod the method where the exception was raised, possibly {@code null}
  * @param exception the raised exception
  * @return a method to handle the exception, or {@code null}
  */
 protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
     HandlerMethod handlerMethod, Exception exception) {
   if (handlerMethod != null) {
     Class<?> handlerType = handlerMethod.getBeanType();
     ExceptionHandlerMethodResolver resolver = this.exceptionHandlersByType.get(handlerType);
     if (resolver == null) {
       resolver = new ExceptionHandlerMethodResolver(handlerType);
       this.exceptionHandlersByType.put(handlerType, resolver);
     }
     Method method = resolver.resolveMethod(exception);
     if (method != null) {
       return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
     }
   }
   return getGlobalExceptionHandlerMethod(exception);
 }