🍅一、BasicErrorController
☘️1.1 描述
BasicErrorController是Springboot中默认的异常处理方法,无需额外的操作,当程序发生了异常之后,Springboot自动捕获异常,重新请求到BasicErrorController中,在BasicErrorController中返回一个视图页面。
🌱1.2 原理解析-配置
在ErrorMvcAutoConfiguration会配置Springboot中关于异常相关的类。其中有两个类是异常相关的。
1.2.1 BasicErrorController
配制的第一个Bean是BasicErrorController类,所有的异常捕获的时候,都会重新请求到这个Controller。
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
                                                 ObjectProvider<ErrorViewResolver> errorViewResolvers) {
  return new BasicErrorController(errorAttributes, this.serverProperties.getError(),                            errorViewResolvers.orderedStream().collect(Collectors.toList()));
}
1.2.2 ErrorPageCustomizer
初始化第二个Bean是ErrorPageCustomizer类,这个类是发生异常之后返回视图的模板页面
// 初始化一个ErrorPageCustomizer类
@Bean
public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
  return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
}
// ErrorPageCustomizer类主要的作用
static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
  private final ServerProperties properties;
  private final DispatcherServletPath dispatcherServletPath;
  protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {
    this.properties = properties;
    this.dispatcherServletPath = dispatcherServletPath;
  }
  @Override
  public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
    ErrorPage errorPage = new ErrorPage(
      // 这里获取异常页面的路径,默认是在/error路径下面
      this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
    errorPageRegistry.addErrorPages(errorPage);
  }
  @Override
  public int getOrder() {
    return 0;
  }
}
🌲1.3 原理解析-触发
当异常发生后会重新请求到BasicErrorController中,该类有两个接口一个是针对json的请求,一个是针对text/html的请求。这里主要看text/html的请求,返回一个ModelAndView。返回什么ModelAndView页面,是通过resolveErrorView方法去解析的。如果没有ModelAndView视图,就自己创建一个默认的返回。
// 处理针对请求是text/html的请求
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
  HttpStatus status = getStatus(request);
  Map<String, Object> model = Collections
    .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
  response.setStatus(status.value());
  ModelAndView modelAndView = resolveErrorView(request, response, status, model);
  return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
// 处理请求是json的请求
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
  HttpStatus status = getStatus(request);
  if (status == HttpStatus.NO_CONTENT) {
    return new ResponseEntity<>(status);
  }
  Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
  return new ResponseEntity<>(body, status);
}
- resolveErrorView
这里先是获取所有的ErrorViewResolver,然后循环去调用ErrorViewResolver的resolveErrorView方法。ErrorViewResolver是一个功能性接口,只有一个方法resolveErrorView。
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,
                                        Map<String, Object> model) {
  for (ErrorViewResolver resolver : this.errorViewResolvers) {
    ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
    if (modelAndView != null) {
      return modelAndView;
    }
  }
  return null;
}
- DefaultErrorViewResolver
ErrorViewResolver只有一个实现类DefaultErrorViewResolver,在DefaultErrorViewResolver的resolverErrorView方法中先调用了resolve方法,这里传的参数是响应吗,比如500,404等。这个方法的目的是找到响应吗对应的页面比如500.html、404.html等,如果没找到就找5xx.html页面。
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
  // 这里直接用500来解析是否有这个页面
  ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
  // 如果是空的,就用5xx,去看看有没有这个页面
  if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
    modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
  }
  return modelAndView;
}
- resolve
resolve方法找到error路径下对应的页面,如果还是不存在就调用resolveResource方法去找。
private ModelAndView resolve(String viewName, Map<String, Object> model) {
  String errorViewName = "error/" + viewName;
  TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
                                                                                         this.applicationContext);
  if (provider != null) {
    return new ModelAndView(errorViewName, model);
  }
  return resolveResource(errorViewName, model);
}
- resolveResource
resolveResource方法先通过getStaticLocations()获取静态文件路径,然后去路径下判断是否有异常页面存在。
getStaticLocations()包含了四个路径:
1.classpath:/METAINF/resources/
2.classpath:/resources
3.classpath:/static/
4.classpath:/public/
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
  	// 获取静态资源路径
		for (String location : this.resources.getStaticLocations()) {
			try {
				Resource resource = this.applicationContext.getResource(location);
				resource = resource.createRelative(viewName + ".html");
				if (resource.exists()) {
					return new ModelAndView(new HtmlResourceView(resource), model);
				}
			}
			catch (Exception ex) {
			}
		}
		return null;
	}
🍇二、HandlerExceptionResolver
🌳1.1描述
HandlerExceptionResolver是通过定义一个类实现HandlerExceptionResolver接口,然后重写resolveException方法,这个方法返回一个ModelAndView类。在ModelAndView中可以定义一些异常相关的处理。
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("error", "报错了");
        mv.addObject("status", "saliwa");
        return mv;
    }
}
🌴1.2原理解析-触发
- doDispatch
要解析HandlerExceptionResolver的原理要从DispatcherServlet的doDispatch开始,我们先看一下这个方法的主要流程,省略掉部分无关代码。先去执行对应请求的方法,如果方法里面发生异常捕获异常,无论是否发生异常都会执行processDispatchResult。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
			try {
				// 去执行我们要调用的方法,比如我们请求的某个controller方法,如果在这个controller执行过程中
        // 发生了异常或者错误都在这里捕获的,并且用dispatchException这个变量来接收返回的异常类
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			}catch (Exception ex) {
				dispatchException = ex;
			}catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
      // 这个方法无论是否有异常都会执行
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
	}
- processDispatchResult
processDispatchResult方法,这里省略部分无关代码,首先判读异常是否不为空,如果不为空就执行异常处理逻辑,调用processHandlerException方法。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
  boolean errorView = false;
	// 异常不为空
  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);
    }
  }
}
- processHandlerException
这里主要是去找有没有HandlerExceptionResolver类,如果有,就执行他的resolveException方法,这个方法会返回ModelAndView,如果ModelAndView不为空就返回。
前面我们自定义的类就是实现了HandlerExceptionResolver接口,并且重写了resolveException方法,返回了一个ModelAndView,这里异常就处理结束了。
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {
  ModelAndView exMv = null;
  if (this.handlerExceptionResolvers != null) {
    // 这里就是我们自己的实现类,包括两个默认的和我们自己定义的
    for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
      exMv = resolver.resolveException(request, response, handler, ex);
      if (exMv != null) {
        break;
      }
    }
  }
  if (exMv != null) {
    return exMv;
  }
  throw ex;
}
🍈三、ControllerAdvice
🌵3.1 描述
ControllerAdvice是全局异常拦截器,配合ExceptionHandler使用。除了可以拦截Java定义的异常,还可以自定义异常。
先自定义一个异常
@Getter
@Setter
public class MyException extends RuntimeException {
    private String errorCode;
    private String errorMessage;
    public MyException () {
        super();
    }
    public MyException(String errorCode, String errorMessage) {
        super(errorMessage);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }
}
自定义一个全局异常拦截器,拦截自定义的异常和空指针异常。
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
  	// 自定异常
    @ExceptionHandler(value = MyException.class)
    public Object restErrorHandler(HttpServletRequest request, MyException e) {
        log.error("报错了: ", e);
        return "错误码:" + e.getErrorCode() + "错误内容:" + e.getErrorMessage();
    }
		// 空指针异常
    @ExceptionHandler(value={java.lang.NullPointerException.class})
    public String nullPointerExceptionHandler(Exception e){
        log.error("报错了 ", e);
        return "错误内容:" + e.getMessage();
    }
}
🌾3.2 原理解析-配置
3.2.1 WebMvcConfigurationSupport
- handlerExceptionResolver
在WebMvcConfigurationSupport类里面要初始化一个HandlerExceptionResolver类,在初始化这个类之后要,要执行addDefaultHandlerExceptionResolvers方法。
@Bean
public HandlerExceptionResolver handlerExceptionResolver(
  @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
  List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
  configureHandlerExceptionResolvers(exceptionResolvers);
  if (exceptionResolvers.isEmpty()) {
    addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
  }
  extendHandlerExceptionResolvers(exceptionResolvers);
  HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
  composite.setOrder(0);
  composite.setExceptionResolvers(exceptionResolvers);
  return composite;
}
- addDefaultHandlerExceptionResolvers
在这个方法里面先调用了createExceptionHandlerExceptionResolver方法创建了ExceptionHandlerExceptionResolver类。
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
			ContentNegotiationManager mvcContentNegotiationManager) {
		ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
		exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
		exceptionHandlerResolver.setMessageConverters(getMessageConverters());
		exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
		exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
		if (jackson2Present) {
			exceptionHandlerResolver.setResponseBodyAdvice(
					Collections.singletonList(new JsonViewResponseBodyAdvice()));
		}
		if (this.applicationContext != null) {
			exceptionHandlerResolver.setApplicationContext(this.applicationContext);
		}
		exceptionHandlerResolver.afterPropertiesSet();
		exceptionResolvers.add(exceptionHandlerResolver);
		ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
		responseStatusResolver.setMessageSource(this.applicationContext);
		exceptionResolvers.add(responseStatusResolver);
		exceptionResolvers.add(new DefaultHandlerExceptionResolver());
	}
3.2.2 ExceptionHandlerExceptionResolver
- afterPropertiesSet
在这个方法里面主要是调用了一个方法initExceptionHandlerAdviceCache
@Override
	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBodyAdvice beans
		initExceptionHandlerAdviceCache();
		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}
- initExceptionHandlerAdviceCache
这个方法是最重要的,首先获取有ControllerAdvice这个注解的bean,也就是我们自定义的全局异常拦截器,然后将这个bean转换成ExceptionHandlerMethodResolver 。
private void initExceptionHandlerAdviceCache() {
		if (getApplicationContext() == null) {
			return;
		}
	// 获取有ControllerAdvice注解的类
		List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		for (ControllerAdviceBean adviceBean : adviceBeans) {
			Class<?> beanType = adviceBean.getBeanType();
			if (beanType == null) {
				throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
			}
			ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
			if (resolver.hasExceptionMappings()) {
				this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
			}
			if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
				this.responseBodyAdvice.add(adviceBean);
			}
		}
		if (logger.isDebugEnabled()) {
			int handlerSize = this.exceptionHandlerAdviceCache.size();
			int adviceSize = this.responseBodyAdvice.size();
			if (handlerSize == 0 && adviceSize == 0) {
				logger.debug("ControllerAdvice beans: none");
			}
			else {
				logger.debug("ControllerAdvice beans: " +
						handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
			}
		}
	}
🌿3.3 原理解析-触发
3.3.1 DispatcherServlet
- doDispatch
要解析HandlerExceptionResolver的原理要从DispatcherServlet的doDispatch开始,我们先看一下这个方法的主要流程,省略掉部分无关代码。先去执行对应请求的方法,如果方法里面发生异常捕获异常,无论是否发生异常都会执行processDispatchResult。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
			try {
				// 去执行我们要调用的方法,比如我们请求的某个controller方法,如果在这个controller执行过程中
        // 发生了异常或者错误都在这里捕获的,并且用dispatchException这个变量来接收返回的异常类
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			}catch (Exception ex) {
				dispatchException = ex;
			}catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
      // 这个方法无论是否有异常都会执行
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
	}
- processDispatchResult
processDispatchResult方法,这里省略部分无关代码,首先判读异常是否不为空,如果不为空就执行异常处理逻辑,调用processHandlerException方法。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
  boolean errorView = false;
	// 异常不为空
  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);
    }
  }
}
- processHandlerException
这里主要是去找有没有HandlerExceptionResolver类,如果有,就执行他的resolveException方法,这个方法会返回ModelAndView,如果ModelAndView不为空就返回。
这里我们没有自定义HandlerExceptionResolver,只有DefaultErrorAttributes和HandlerExceptionResolverComposite。
这里先去调用DefaultErrorAttributes的resolverException方法,这个方法返回的是null,然后会继续调用HandlerExceptionResolverComposite类的resolverException方法。
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {
  ModelAndView exMv = null;
  if (this.handlerExceptionResolvers != null) {
    for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
      exMv = resolver.resolveException(request, response, handler, ex);
      if (exMv != null) {
        break;
      }
    }
  }
  if (exMv != null) {
    return exMv;
  }
  throw ex;
}
3.3.2 HandlerExceptionResolverComposite
- resolveException
在这个方法里面会继续找HandlerExceptionResolver,并执行resolve Exception方法。这里的HandlerExceptionResolver有三个ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver。
public ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
  if (this.resolvers != null) {
    for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
      ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
      if (mav != null) {
        return mav;
      }
    }
  }
  return null;
}
首先调用的是ExceptionHandlerExceptionResolver的resolveException方法,但是这个方法没有resolveException方法,但是它的抽象父类的父类AbstractHandlerExceptionResolver有这个方法
3.3.3AbstractHandlerExceptionResolver
- resolveException
这里的resolveException方法中,调用了doResolveException去获取ModelAndView
public ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
  if (shouldApplyTo(request, handler)) {
    prepareResponse(ex, response);
    ModelAndView result = doResolveException(request, response, handler, ex);
    if (result != null) {
      // Print debug message when warn logger is not enabled.
      if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
        logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
      }
      // Explicitly configured warn logger in logException method.
      logException(ex, request);
    }
    return result;
  }
  else {
    return null;
  }
}
- doResolveException
ExceptionHandlerExceptionResolver也没有 doResolveException但是它的父类AbstractHandlerMethodExceptionResolver有这个方法。
3.3.4 AbstractHandlerMethodExceptionResolver
- doResolveException
这里走的是AbstractHandlerMethodExceptionResolver类的doResolveException方法,这个方法会继续调用doResolveHandlerMethodException,这个方法是在ExceptionHandlerExceptionResolver里面。
@Override
@Nullable
protected final ModelAndView doResolveException(
  HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
  HandlerMethod handlerMethod = (handler instanceof HandlerMethod ? (HandlerMethod) handler : null);
  return doResolveHandlerMethodException(request, response, handlerMethod, ex);
}
3.3.5 ExceptionHandlerExceptionResolver
- doResolveHandlerMethodException
看下ExceptionHandlerExceptionResolver实现的doResolveHandlerMethodException,这里最重要的是第一行getExceptionHandlerMethod,获取异常处理方法
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
			HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
		ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
		if (exceptionHandlerMethod == null) {
			return null;
		}
		if (this.argumentResolvers != null) {
			exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		ArrayList<Throwable> exceptions = new ArrayList<>();
		try {
			if (logger.isDebugEnabled()) {
				logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
			}
			// Expose causes as provided arguments as well
			Throwable exToExpose = exception;
			while (exToExpose != null) {
				exceptions.add(exToExpose);
				Throwable cause = exToExpose.getCause();
				exToExpose = (cause != exToExpose ? cause : null);
			}
			Object[] arguments = new Object[exceptions.size() + 1];
			exceptions.toArray(arguments);  // efficient arraycopy call in ArrayList
			arguments[arguments.length - 1] = handlerMethod;
			exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);
		}
		catch (Throwable invocationEx) {
			// Any other than the original exception (or a cause) is unintended here,
			// probably an accident (e.g. failed assertion or the like).
			if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {
				logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
			}
			// Continue with default processing of the original exception...
			return null;
		}
		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();
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
			return mav;
		}
	}
- getExceptionHandlerMethod
可以看出这里是先找到有ControllerAdvicebean的类,然后根据异常类型去匹配这个bean里面定义的ExceptionHandler,这里就找到我们自己定义的全局异常处理的ExceptionHandler,进行异常处理。
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
			@Nullable HandlerMethod handlerMethod, Exception exception) {
  Class<?> handlerType = null;
  if (handlerMethod != null) {
    // Local exception handler methods on the controller class itself.
    // To be invoked through the proxy, even in case of an interface-based proxy.
    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);
    if (method != null) {
      return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
    }
    // For advice applicability check below (involving base packages, assignable types
    // and annotation presence), use target class instead of interface-based proxy.
    if (Proxy.isProxyClass(handlerType)) {
      handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
    }
  }
  for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
    ControllerAdviceBean advice = entry.getKey();
    if (advice.isApplicableToBeanType(handlerType)) {
      ExceptionHandlerMethodResolver resolver = entry.getValue();
      Method method = resolver.resolveMethod(exception);
      if (method != null) {
        return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
      }
    }
  }
  return null;
}
3.3.6 调用时序图
这里的整体调用逻辑如下:
1.首先调用dispatch去执行方法执行目标方法
2.执行完毕之后调用processDispatchResult去处理执行结果
3.如果目标方法抛出了一场就执行processHandlerException去处理异常
4.在processHandlerException中会调用HandlerExceptionResolverComposite的resolveException方法
5.在HandlerExceptionResolverComposite的resolveException方法会继续调用resolveExeption方法
6.这里是调用ExceptionHandlerExceptionResolver的resolveException方法
7.但是ExceptionHandlerExceptionResolver没有resolveException方法,但是它父类的父类AbstractHandlerExceptionResolver有这个方法
8.然后在resolveException中又调用了doResolveException方法,ExceptionHandlerExceptionResolver没有这个方法,但是它的父类有
9.在AbstractHandlerMethodExceptionResolver的doResolveException方法中调用了doResolveHandlerMethodException
10.doResolveHandlerMethodException是ExceptionHandlerExceptionResolver的,里面继续调用了getExceptionHandlerMethod方法
11.整个调用链路就完成了




















