spring源碼解析-web系列(一):啓動
spring源碼解析-web系列(二):處理請求的過程
spring源碼解析-web系列(三):九大組件之HandlerMapping
spring源碼解析-web系列(四):九大組件之HandlerAdapter
spring源碼解析-web系列(五):解析請求參數
spring源碼解析-web系列(六):九大組件之ViewResolver
spring源碼解析-web系列(七):九大組件之HandlerExceptionResolver
轉載請標明出處:
https://blog.csdn.net/bingospunky/article/details/98947965
本文出自馬彬彬的博客
HandlerExceptionResolver
HandlerExceptionResolver用來解析請求處理過程中產生的異常。
HandlerExceptionResolver的繼承關係如下:
圖1:
1.AbstractHandlerExceptionResolver:是所有直接解析異常類的父類,定義了通用的解析流程。
2.AbstractHandlerMethodExceptionResolver:和其子類ExceptionHandlerExceptionResolver一起完成對@ExceptionHandler註解方法的處理。
3.DefaultHandlerExceptionResolver:根據異常的類型不同進行處理。
4.ResponseStatusExceptionResolver:解析@ResponseStatus註解類型的異常。
5.SimpleMappingExceptionResolver:通過配置異常類型和View的對應關係解析異常。
本文只介紹一下 AbstractHandlerMethodExceptionResolver 和它的子類,因爲@ExceptionHandler是我們常用的異常處理方式,其他的方式很少使用。
AbstractHandlerExceptionResolver
代碼1 (org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException):
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
if (shouldApplyTo(request, handler)) {
// Log exception, both at debug log level and at warn level, if desired.
if (this.logger.isDebugEnabled()) {
this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
}
logException(ex, request);
prepareResponse(ex, response);
return doResolveException(request, response, handler, ex);
}
else {
return null;
}
}
代碼1第5行判斷是否可以解析這個handler拋出的異常,如果可以解析,則通過代碼1第12行調用模板方法doResolveException讓子類去解析該異常。判斷是否可以解析handler的異常的邏輯也很簡單:默認可以即系所有handler的異常;如果用戶指定了某些handler對象或handler類型,則以用戶指定的爲準。
AbstractHandlerMethodExceptionResolver
AbstractHandlerMethodExceptionResolver是AbstractHandlerExceptionResolver的子類,主要工作就是對於handler是HandlerMethod時做一些適配。因爲在父類的shouldApplyTo中,handler是對象,而對於HandlerMethod,handler是方法,該類通過handler獲取它所在的Controller對象給父類的shouldApplyTo使用。該類剩下的一個工作就是抽象了一個 doResolveHandlerMethodException 供子類來解析異常使用。
ExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver解析Exception的過程如下:
代碼2 (org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException):
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
ServletInvocableHandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
} else {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
}
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception});
} catch (Exception var9) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, var9);
}
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
} else {
ModelAndView mav = (new ModelAndView()).addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View)mavContainer.getView());
}
return mav;
}
}
}
代碼2第2行通過handlerMethod和exception獲取到ServletInvocableHandlerMethod對象,ServletInvocableHandlerMethod對象就是我們 @ExceptionHandler 註解標記的方法。後面的過程和 RequestMappingHandlerAdapter 裏的處理流程相似,就像是一個簡化版的 RequestMappingHandlerAdapter ,因爲他們都是controller裏的處理邏輯,區別只是一個處理異常,一個處理用戶的請求而已,這裏就不展開介紹了,可以參考博客 spring源碼解析-web系列(四):九大組件之HandlerAdapter 。
代碼2第2行通過handlerMethod和exception獲取到ServletInvocableHandlerMethod對象的過程如下:
代碼3 (org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.getExceptionHandlerMethod):
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = handlerMethod != null ? handlerMethod.getBeanType() : null;
if (handlerMethod != null) {
ExceptionHandlerMethodResolver resolver = (ExceptionHandlerMethodResolver)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);
}
}
Iterator var8 = this.exceptionHandlerAdviceCache.entrySet().iterator();
while(var8.hasNext()) {
Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry = (Entry)var8.next();
if (((ControllerAdviceBean)entry.getKey()).isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = (ExceptionHandlerMethodResolver)entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(((ControllerAdviceBean)entry.getKey()).resolveBean(), method);
}
}
}
return null;
}
代碼3第2第14行獲取當前handlerMethod所在的類裏註解了@ExceptionHandler的匹配的方法。代碼2第16第27行獲取被@ControllerAdvice註解的類裏註解了@ExceptionHandler的匹配的方法。
在獲取註解了@ExceptionHandler的匹配的方法時,使用的是 ExceptionHandlerMethodResolver 這個類。正如代碼3第10行,ExceptionHandlerMethodResolver的resolveMethod方法的處理過程:首先獲取類裏所有註解了@ExceptionHandler的方法,然後根據@ExceptionHandler方法能處理的異常類型和當前實際的異常類型進行匹配。
總結
HandlerExceptionResolver的作用就是解析異常,具體工作就是:將異常相關信息設置到Model、給response設置響應屬性。
mvc:annotation-driven 會將ExceptionHandlerExceptionResolver、DefaultHandlerExceptionResolver、ResponseStatusExceptionResolver設置到DispatcherServlet的handlerExceptionResolvers屬性中。
HandlerExceptionResolver只處理請求處理過程中的異常,異常處理本身拋出的異常和渲染視圖的異常不歸它處理。