spring源碼解析-web系列(七):九大組件之HandlerExceptionResolver

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:

https://qingtian-blog.oss-cn-beijing.aliyuncs.com/spring%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-web%E7%B3%BB%E5%88%97%28%E4%B8%83%29%3A%E4%B9%9D%E5%A4%A7%E7%BB%84%E4%BB%B6%E4%B9%8BHandlerExceptionResolver_1.png

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只處理請求處理過程中的異常,異常處理本身拋出的異常和渲染視圖的異常不歸它處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章