spring mvc DispatcherServlet 接收請求到響應數據的過程

以前閱讀過DispatcherServlet的doService(HttpServletRequest, HttpServletResponse)和doDispatch(HttpServletRequest, HttpServletResponse)的源碼,後來過了一段時間後又忘了,所以把這次閱讀的內容寫一篇博客,記錄下來。

首先看doService(HttpServletRequest, HttpServletResponse)方法中的源碼;

@Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
            logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                    " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }

        // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<String, Object>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        // Make framework objects available to handlers and view objects.
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

        try {
            doDispatch(request, response);
        }
        finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

在該方法中,大部分都是設置request的屬性值,方便後面的方法獲取相應的對象,我們可能會需要獲取的屬性名爲DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE的HttpServletRequest屬性值,它是當前mvc的WebApplicationContext對象。下面看doDispatch(HttpServletRequest, HttpServletResponse)方法。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                //檢查是否Multipart請求(文件上傳),如果是,調用MultipartResolver.resolveMultipart(HttpServletRequest)解析Multipart資源,返回一個適配類MultipartHttpServletRequest,否則返回參數中的request。
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                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));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }
  1. 解析文件上傳
    在這個方法中去調用checkMultipart(HttpServletRequest),再去調用multipartResolver.resolveMultipart(request)解析Multipart資源,實際上這個方法返回了一個MultipartHttpServletRequest接口的實現對象,MultipartHttpServletRequest接口繼承了HttpServletRequest和MultipartRequest,獲取文件的操作都在MultipartRequest中定義。

    protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
            if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
                logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
                        "this typically results from an additional MultipartFilter in web.xml");
            }
            else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) {
                logger.debug("Multipart resolution failed for current request before - " +
                        "skipping re-resolution for undisturbed error rendering");
            }
            else {
                //解析
                return this.multipartResolver.resolveMultipart(request);
            }
        }
        // If not returned before: return original request.
        return request;
    }

    其中MultipartResolver默認有兩個實現類,分別是CommonsMultipartResolver和StandardServletMultipartResolver,要想了解具體的實現可以去看這兩個類的源碼。

  2. 獲取Handler
    解析multipart資源後,就是要找到hander對象,以及攔截器鏈。調用HandlerMapping#getHandler(HttpServletRequest request)方法去查找,返回HandlerExecutionChain對象。

    在xml中使用\會自動加載RequestMappingHandlerMapping和BeanNameUrlHandlerMapping,HandlerAdapter,HandlerMethodArgumentResolver,HttpMessageConverter,以及其它相關類,如果有興趣瞭解的,可以去看類org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser的源碼。

    HandlerMapping有幾個子類是在初始化時默認加載的:

    • RequestMappingHandlerMapping
      掃描@Controller標註的類中,獲取@RequestMapping註解的類實例和方法,創建RequestMappingInfo(請求映射信息)對象。

    • BeanNameUrlHandlerMapping
      這是Dispatcher Servlet的默認的HandlerMapping,所以在應用上下文配置文件中簡單地用“Url樣式”來定義一個控制器Bean的名字,就可以告訴Dispatcher Servlet什麼樣式的請求應該由哪個控制器去處理,而不用顯式地定義一個HandlerMapping。
      例:若控制器ListCoursesController的URL樣式是“listCourses.go”, 示例如下:

<bean name="/listCourses.go"  class="com.w3cs.vlar.ListCoursesController"> 
        <property name="couseService">              
            <ref bean="courceService"/>         
        </property>     
</bean> 
    ```


    在doDispatch(HttpServletRequest, HttpServletResponse)方法中去遍歷handlerMappings字段,它是一個List<HandlerMapping>對象,調用HandlerMapping#getHandler(HttpServletRequest),該方法的代碼如下:
    ```java
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //獲取請求對應的HandlerMethod對象。
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            //如果沒有找到,獲取默認的handlerMethod對象
            handler = getDefaultHandler();
        }
        //如果沒有,返回null
        if (handler == null) {
            return null;
        }
        //獲取Hander對象
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }

        //獲取Handler對象執行鏈,把匹配的攔截器加入到執行鏈中。
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        //通過header中的Origin字段防cors攻擊
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }
    ```
獲取請求對應的HandlerMethod對象的getHandlerInternal(HttpServletRequest)最終會進入到lookupHandlerMethod(Strin, HttpServletRequest)方法中。
通過requestUri查找RequestMappingInfo對象。如果沒有找到,將所有的RequestMappingInfo對象都加入到列表中。然後按@RequestMapping匹配度進行排序,取匹配度最高的。

    ```java
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
            List<Match> matches = new ArrayList<Match>();
            //
            List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
            if (directPathMatches != null) {
                addMatchingMappings(directPathMatches, matches, request);
            }
            if (matches.isEmpty()) {
                // No choice but to go through all mappings...
                addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
            }

            if (!matches.isEmpty()) {

                Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                //排序
                Collections.sort(matches, comparator);
                if (logger.isTraceEnabled()) {
                    logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
                            lookupPath + "] : " + matches);
                }
                Match bestMatch = matches.get(0);
                if (matches.size() > 1) {
                    if (CorsUtils.isPreFlightRequest(request)) {
                        return PREFLIGHT_AMBIGUOUS_MATCH;
                    }
                    Match secondBestMatch = matches.get(1);
                    if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                        Method m1 = bestMatch.handlerMethod.getMethod();
                        Method m2 = secondBestMatch.handlerMethod.getMethod();
                        throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                                request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
                    }
                }
                handleMatch(bestMatch.mapping, lookupPath, request);
                return bestMatch.handlerMethod;
            }
            else {
                return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
            }
        }
    ```
RequestMapping匹配內容的排序規則如下:

    如果是HEAD提交方式,先去比較提交方式,比較GET和HEAD,GET優先。

    比較path,比較path和requestUri的匹配度

    比較參數 params 匹配數量,多排前面

    比較請求匹配數量,多排前面

    比較request Content-Type的內容,匹配上的排前面

    比較request Accept的內容,匹配上的排前面

    比較請求方式,匹配上的排前面

    最後一種是比較自定義的匹配條件。

    ```java
    public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
            int result;
            // Automatic vs explicit HTTP HEAD mapping
            if (HttpMethod.HEAD.matches(request.getMethod())) {
                result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
                if (result != 0) {
                    return result;
                }
            }
            result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.producesCondition.compareTo(other.getProducesCondition(), request);
            if (result != 0) {
                return result;
            }
            // Implicit (no method) vs explicit HTTP method mappings
            result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
            if (result != 0) {
                return result;
            }
            return 0;
        }
    ```




    這是獲取Handler執行鏈的方法,可以看到裏面是去匹配攔截器指定攔截的路徑,如果匹配上,就加入執行鏈中。
    ```java
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
            HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                    (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

            String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
            for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
                if (interceptor instanceof MappedInterceptor) {
                    MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                    if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                        chain.addInterceptor(mappedInterceptor.getInterceptor());
                    }
                }
                else {
                    chain.addInterceptor(interceptor);
                }
            }
            return chain;
        }
    ```
3. 獲取HandlerAdapter

    執行完上面的代碼後取得HandlerMethod,然後再去獲取HandlerAdapter對象,HandlerAdapter字面上的意思就是處理適配器,它的作用用一句話概括就是調用具體的方法對用戶發來的請求進行處理。當handlerMapping獲取到執行請求的HandlerMetho時,DispatcherServlte會根據HandlerMethod對應的類型來調用相應的HandlerAdapter來進行處理。

    DispatcherServlte會根據配置文件信息註冊HandlerAdapter,如果在配置文件中沒有配置,那麼DispatcherServlte會獲取HandlerAdapter的默認配置,如果是讀取默認配置的話,DispatcherServlte會讀取DispatcherServlte.properties文件,該文件中配置了三種HandlerAdapter:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter。DispatcherServlte會將這三個HandlerAdapter對象存儲到它的handlerAdapters這個集合屬性中,這樣就完成了HandlerAdapter的註冊。
幾種適配器對應的處理器以及這些處理器的作用\
        1. AnnotationMethodHandlerAdapter主要是適配註解類處理器,註解類處理器就是我們經常使用的@Controller的這類處理器。在spring3.2之後改用了RequestMappingHandlerAdapter,RequestMappingHandlerAdapter是在解析\<mvc:annotation-driven>時由org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser自動加載的。
        2. HttpRequestHandlerAdapter主要是適配靜態資源處理器,靜態資源處理器就是實現了HttpRequestHandler接口的處理器,這類處理器的作用是處理通過SpringMVC來訪問的靜態資源的請求。        
        3.SimpleControllerHandlerAdapter是Controller處理適配器,適配實現了Controller接口或Controller接口子類的處理器,比如我們經常自己寫的Controller來繼承MultiActionController,那麼自己寫的這些Controller就會由SimpleControllerHandlerAdapter來適配。
        4.SimpleServletHandlerAdapter是Servlet處理適配器,適配實現了Servlet接口或Servlet的子類的處理器,我們不僅可以在web.xml裏面配置Servlet,其實也可以用SpringMVC來配置Servlet,不過這個適配器很少用到,而且SpringMVC默認的適配器沒有他,默認的是前面的三種。

獲取HandlerAdapter的代碼如下:
```java
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        for (HandlerAdapter ha : this.handlerAdapters) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing handler adapter [" + ha + "]");
            }
            if (ha.supports(handler)) {
                return ha;
            }
        }
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }




<div class="se-preview-section-delimiter"></div>

方法內部運行一個for循環,然後通過supports(Object)方法去判斷是否支持,如果支持,就返回這個HandlerAdapter。下面列出RequestMappingHandlerAdapter#supports(Object)的代碼。

RequestMappingHandlerAdapter是繼承AbstractHandlerMethodAdapter,但是AbstractHandlerMethodAdapter中有一個抽象方法supportsInternal(HandlerMethod)是由子類去實現的。

AbstractHandlerMethodAdapter#supports(Object)

public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }




<div class="se-preview-section-delimiter"></div>

RequestMappingHandlerAdapter#supportsInternal(HandlerMethod)

protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }




<div class="se-preview-section-delimiter"></div>

RequestMappingHandlerAdapter#supportsInternal(HandlerMethod)方法沒有做任何其它的判斷了,直接返回true。這就是我們通過@Controller註解定義的Action查找HandlerAdapter的邏輯。


4. 執行攔截器的preHandle(HttpServletRequest, HttpServletResponse, Object)方法
doDispatcher()方法中會執行HandlerExecutionChain#applyPreHandle(HttpServletRequest, HttpServletResponse)方法,它類似於責任鏈,如果其中有一個攔發器返回false,表示請求被攔截,然後執行攔截器的afterCompletion()方法。再結束doDispatch方法。

doDispatch(HttpServletRequest, HttpServletResponse) 方法中的代碼段

    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }




<div class="se-preview-section-delimiter"></div>

HandlerExecutionChain#applyPreHandle(HttpServletRequest, HttpServletResponse)

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    //如果失敗,直接調用攔截器的afterCompletion()方法
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }




<div class="se-preview-section-delimiter"></div>



5. 執行HandlerAdapter
這裏以RequestMappingHandlerAdapter爲例,瞭解一下HandlerAdapter處理請求的一個過程,以及直接返回json是如何處理的。

調用RequestMappingHandlerAdapter#handler(HttpServletRequest, HttpServletResponse, Object)方法後,會執行到invokeHandlerMethod(HttpServletRequest,HttpServletResponse, HandlerMethod),在這個方法會先組裝模型,然後執行invocableMethod.invokeAndHandle(webRequest, mavContainer)

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                if (logger.isDebugEnabled()) {
                    logger.debug("Found concurrent result value [" + result + "]");
                }
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }




<div class="se-preview-section-delimiter"></div>

ServletInvocableHandlerMethod#invokeAndHandle(ServletWebRequest, ModelAndViewContainer, Object…)

public void invokeAndHandle(ServletWebRequest webRequest,
            ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

        //解析參數,執行HanderMethod
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        else if (StringUtils.hasText(this.responseReason)) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        try {
            //處理返回結果(比如返回json)
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
            }
            throw ex;
        }
    }




<div class="se-preview-section-delimiter"></div>

先來看看invokeForRequest()方法是怎麼解析參數,然後執行Controller裏面的方法的。

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        //獲取方法參數值
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                    "' with arguments " + Arrays.toString(args));
        }
        //執行method方法,獲得返回值
        Object returnValue = doInvoke(args);
        if (logger.isTraceEnabled()) {
            logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                    "] returned [" + returnValue + "]");
        }
        return returnValue;
    }




<div class="se-preview-section-delimiter"></div>

上面的方法邏輯是先獲取方法參數值,然後執行Controller中的方法,獲得返回值。
先看獲取方法參數值的流程。

HandlerMethodArgumentResolverComposite

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        //獲取方法參數解析器
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
        }
        //解析參數
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }


    //獲取方法參數解析器的方法
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
        if (result == null) {
            for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
                            parameter.getGenericParameterType() + "]");
                }
                if (methodArgumentResolver.supportsParameter(parameter)) {
                    result = methodArgumentResolver;
                    this.argumentResolverCache.put(parameter, result);
                    break;
                }
            }
        }
        return result;
    }




<div class="se-preview-section-delimiter"></div>

HandlerMethodArgumentResolver是處理器方法參數解析器,它只有兩個方法,判斷是否支持解析指定的參數,和解析參數。看源碼:

public interface HandlerMethodArgumentResolver {


    boolean supportsParameter(MethodParameter parameter);


    Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;

}




<div class="se-preview-section-delimiter"></div>

HandlerMethodArgumentResolver實現了很多方案,因此也有不少子類:

  • RequestParamMethodArgumentResolver
    支持兩種場景。一種是含@RequestParam註解的參數,另一種就是簡單類型,如Integer、String、Date、URI, URL,Locale等

  • RequestParamMapMethodArgumentResolver
    用來獲取所有的header信息。
    參數類型可以是普通的Map類型,也可以是MultiValueMap或者進一步的Http請求參數,他們與普通Map類型的區別是他們對value值後者們是以List形式存放,前者是以String形式存放。

  • RequestHeaderMethodArgumentResolver
    主要用來處理含有@RequestHeader註解的參數,但同時該參數又不是Map類型。

    使用場景:

    @RequestMapping(value="/requestHeader",method=RequestMethod.GET)  
    public Map<String,Object> testrequestHeader(@RequestHeader String  Accept){  }
  • RequestHeaderMapMethodArgumentResolver
    用來獲取所有的header信息。
    參數類型可以是普通的Map類型,也可以是MultiValueMap或者進一步的HttpHeaders,他們與普通Map類型的區別是他們對value值後者們是以List形式存放,前者是以String形式存放。 

    使用場景:

    @RequestMapping(value="/requestHeader",method=RequestMethod.GET)    
    public Map<String,Object> testrequestHeader(@RequestHeader Map<String,Object> map1){  }
    
    
    @RequestMapping(value="/requestHeader1",method=RequestMethod.GET)  
    public Map<String,Object> testrequestHeader2(@RequestHeader MultiValueMap<String,Object> map1){  }
  • ServletCookieValueMethodArgumentResolver
    主要處理處理器方法參數中帶有@CookieValue的解析工作。
    它的驗證代碼在AbstractCookieValueMethodArgumentResolver中

    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(CookieValue.class);
    }

    判斷是否支持非常簡單,解析代碼也是一樣的。

    protected Object resolveName(String cookieName, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
        if (Cookie.class.isAssignableFrom(parameter.getNestedParameterType())) {
            return cookieValue;
        }
        else if (cookieValue != null) {
            return this.urlPathHelper.decodeRequestString(servletRequest, cookieValue.getValue());
        }
        else {
            return null;
        }
    }
  • PathVariableMethodArgumentResolver
    主要針對含有@PathVariable的參數。必須要指定@PathVariable的值,即這個ArgumentResolver只能獲取一個uri變量,要想獲取多個則要使用PathVariableMapMethodArgumentResolver。

public boolean supportsParameter(MethodParameter parameter) {
        if (!parameter.hasParameterAnnotation(PathVariable.class)) {
            return false;
        }
        if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
            String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
            return StringUtils.hasText(paramName);
        }
        return true;
    }


    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
                HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
        return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
    }




<div class="se-preview-section-delimiter"></div>
  • PathVariableMapMethodArgumentResolver
    它要求必須含有@PathVariable註解,並且必須是Map類型,並且@PathVariable註解的value沒有值。
public boolean supportsParameter(MethodParameter parameter) {
        PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
        return (ann != null && (Map.class.isAssignableFrom(parameter.getParameterType()))
                && !StringUtils.hasText(ann.value()));
    }

    /**
     * Return a Map with all URI template variables or an empty map.
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        @SuppressWarnings("unchecked")
        Map<String, String> uriTemplateVars =
                (Map<String, String>) webRequest.getAttribute(
                        HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);

        if (!CollectionUtils.isEmpty(uriTemplateVars)) {
            return new LinkedHashMap<String, String>(uriTemplateVars);
        }
        else {
            return Collections.emptyMap();
        }




<div class="se-preview-section-delimiter"></div>
同時我們可以從PathVariableMapMethodArgumentResolver和PathVariableMethodArgumentResolver的源碼中看出,他們的取值都是從request的屬性上進行獲取的webRequest.getAttribute( 

HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);也就是說,在解析完@RequestMapping匹配工作後,便將這些參數設置進request的屬性上,屬性名爲HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE。

  • SessionAttributeMethodArgumentResolver
    解析@SessionAttribute標註的參數。
  • RequestAttributeMethodArgumentResolver
    解析@RequestAttribute標註的參數。
  • MatrixVariableMethodArgumentResolver
    解析@MatrixVariable標註的參數。
  • MatrixVariableMapMethodArgumentResolver
    解析@MatrixVariable標註的參數。

下面我們以RequestParamMethodArgumentResolver爲例,從中瞭解HandlerMethodArgumentResolver的處理過程。
在執行解析的方法時,會調用到父類AbstractNamedValueMethodArgumentResolver#resolveArgument(MethodParameter, ModelAndViewContainer,NativeWebRequest, WebDataBinderFactory)方法,在這個方法中會去調用一個抽象方法resolveName(String, MethodParameter, NativeWebRequest),這個方法是由子類去實現的。

AbstractNamedValueMethodArgumentResolver

public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
        MethodParameter nestedParameter = parameter.nestedIfOptional();

        Object resolvedName = resolveStringValue(namedValueInfo.name);
        if (resolvedName == null) {
            throw new IllegalArgumentException(
                    "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
        }
        //抽象方法,由子類去實現
        Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
        if (arg == null) {
            if (namedValueInfo.defaultValue != null) {
                arg = resolveStringValue(namedValueInfo.defaultValue);
            }
            else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
            }
            arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
        }
        else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
            arg = resolveStringValue(namedValueInfo.defaultValue);
        }

        //會進一步做數據轉換工作,這個步驟以後有時間再瞭解。
        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
            try {
                arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
            }
            catch (ConversionNotSupportedException ex) {
                throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                        namedValueInfo.name, parameter, ex.getCause());
            }
            catch (TypeMismatchException ex) {
                throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                        namedValueInfo.name, parameter, ex.getCause());

            }
        }

        handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

        return arg;
    }


    protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
            throws Exception;

    protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
    }




<div class="se-preview-section-delimiter"></div>

RequestParamMethodArgumentResolver#resolveName(String, MethodParameter, NativeWebRequest)

protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
        MultipartHttpServletRequest multipartRequest =
                WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);

        Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
        if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
            return mpArg;
        }

        Object arg = null;
        if (multipartRequest != null) {
            //獲取文件
            List<MultipartFile> files = multipartRequest.getFiles(name);
            if (!files.isEmpty()) {
                arg = (files.size() == 1 ? files.get(0) : files);
            }
        }
        if (arg == null) {
            //獲取參數值
            String[] paramValues = request.getParameterValues(name);
            if (paramValues != null) {
                arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
            }
        }
        return arg;
    }




<div class="se-preview-section-delimiter"></div>

上面的代碼是處理器方法參數獲取和解析的過程,通過這個過程,拿到最終的參數,調用處理器方法。這一步很簡單了,就是通過java反射實現的了。java.lang.reflect.Method#invode(Object, Object…)。

InvocableHandlerMethod

protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            return getBridgedMethod().invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
            assertTargetBean(getBridgedMethod(), getBean(), args);
            String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(getInvocationErrorMessage(message, args), ex);
        }
        catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
                throw new IllegalStateException(msg, targetException);
            }
        }
    }




<div class="se-preview-section-delimiter"></div>

拿到返回值之後,就調用HandlerMethodReturnValueHandler接口的handleReturnValue(Object, MethodParameter, ModelAndViewContainer, NativeWebRequest)方法去處理返回的數據了。下面給出HandlerMethodReturnValueHandler接口的實現類的源碼。

HandlerMethodReturnValueHandlerComposite

public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

        //獲取返回值處理器
        HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
        if (handler == null) {
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        }

        //處理返回值
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }

    //獲取返回值處理器
    private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
        boolean isAsyncValue = isAsyncReturnValue(value, returnType);
        for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
            if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
                continue;
            }
            if (handler.supportsReturnType(returnType)) {
                return handler;
            }
        }
        return null;
    } 




<div class="se-preview-section-delimiter"></div>

現在返回json數據的應用接口很廣,這裏就給出輸入json和渲染頁面的處理器方法返回值處理器(HandlerMethodReturnValueHandler)。

RequestResponseBodyMethodProcessor

public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        //這裏設置了請求已被處理,在上層方法中將不會返回ModelAndView對象了。
        mavContainer.setRequestHandled(true);
        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

        // Try even with null return value. ResponseBodyAdvice could get involved.
        //將找到返回值轉換
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }




<div class="se-preview-section-delimiter"></div>
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
            ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

        Object outputValue;
        Class<?> valueType;
        Type declaredType;

        if (value instanceof CharSequence) {
            outputValue = value.toString();
            valueType = String.class;
            declaredType = String.class;
        }
        else {
            outputValue = value;
            valueType = getReturnValueType(outputValue, returnType);
            declaredType = getGenericType(returnType);
        }

        HttpServletRequest request = inputMessage.getServletRequest();
        List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
        List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);

        if (outputValue != null && producibleMediaTypes.isEmpty()) {
            throw new IllegalArgumentException("No converter found for return value of type: " + valueType);
        }

        Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
        for (MediaType requestedType : requestedMediaTypes) {
            for (MediaType producibleType : producibleMediaTypes) {
                if (requestedType.isCompatibleWith(producibleType)) {
                    compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
                }
            }
        }
        if (compatibleMediaTypes.isEmpty()) {
            if (outputValue != null) {
                throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
            }
            return;
        }

        List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
        MediaType.sortBySpecificityAndQuality(mediaTypes);

        MediaType selectedMediaType = null;
        for (MediaType mediaType : mediaTypes) {
            if (mediaType.isConcrete()) {
                selectedMediaType = mediaType;
                break;
            }
            else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
                selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                break;
            }
        }

        if (selectedMediaType != null) {
            selectedMediaType = selectedMediaType.removeQualityValue();
            for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
                if (messageConverter instanceof GenericHttpMessageConverter) {
                    if (((GenericHttpMessageConverter) messageConverter).canWrite(
                            declaredType, valueType, selectedMediaType)) {
                        outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
                                (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
                                inputMessage, outputMessage);
                        if (outputValue != null) {
                            addContentDispositionHeader(inputMessage, outputMessage);
                            ((GenericHttpMessageConverter) messageConverter).write(
                                    outputValue, declaredType, selectedMediaType, outputMessage);
                            if (logger.isDebugEnabled()) {
                                logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
                                        "\" using [" + messageConverter + "]");
                            }
                        }
                        return;
                    }
                }
                //判斷是否可以輸入返回值類型和響應的MediaType
                else if (messageConverter.canWrite(valueType, selectedMediaType)) {
                    outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
                            (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
                            inputMessage, outputMessage);
                    if (outputValue != null) {
                        addContentDispositionHeader(inputMessage, outputMessage);
                        //輸出數據,下面給出代碼。 
                        ((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
                                    "\" using [" + messageConverter + "]");
                        }
                    }
                    return;
                }
            }
        }

        if (outputValue != null) {
            throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
        }
    }




<div class="se-preview-section-delimiter"></div>
public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        final HttpHeaders headers = outputMessage.getHeaders();
        //設置響應頭信息
        addDefaultHeaders(headers, t, contentType);


        if (outputMessage instanceof StreamingHttpOutputMessage) {
            StreamingHttpOutputMessage streamingOutputMessage =
                    (StreamingHttpOutputMessage) outputMessage;
            streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
                @Override
                public void writeTo(final OutputStream outputStream) throws IOException {
                    //輸出數據
                    writeInternal(t, new HttpOutputMessage() {
                        @Override
                        public OutputStream getBody() throws IOException {
                            return outputStream;
                        }
                        @Override
                        public HttpHeaders getHeaders() {
                            return headers;
                        }
                    });
                }
            });
        }
        else {
            //輸出數據
            writeInternal(t, outputMessage);
            outputMessage.getBody().flush();
        }
    }




<div class="se-preview-section-delimiter"></div>

輸出json的HttpMessageConverter的輸出數據方法

protected void writeInternal(Object obj, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        String json = "";
        if(customPropertyPreFilter != null){
            json = Json.toJSONStringWithCustom(obj, customPropertyPreFilter, serializerFeature);
        }else{
            json = Json.toJSONString(obj, serializerFeature);
        }
        LOG.debug(json);
        OutputStream out = outputMessage.getBody();
        byte[] bytes = json.getBytes(charset);
        out.write(bytes);
    }




<div class="se-preview-section-delimiter"></div>

數據渲染的處理器方法返回值處理器(HandlerMethodReturnValueHandler)。

ModelAndViewMethodReturnValueHandler

public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

        if (returnValue == null) {
            mavContainer.setRequestHandled(true);
            return;
        }

        ModelAndView mav = (ModelAndView) returnValue;
        if (mav.isReference()) {
            String viewName = mav.getViewName();
            mavContainer.setViewName(viewName);
            if (viewName != null && isRedirectViewName(viewName)) {
            //設置跳轉標記
        mavContainer.setRedirectModelScenario(true);
            }
        }
        else {
            View view = mav.getView();
            mavContainer.setView(view);
            if (view instanceof SmartView) {
                if (((SmartView) view).isRedirectView()) {
                    //設置跳轉標記
                    mavContainer.setRedirectModelScenario(true);
                }
            }
        }
        mavContainer.setStatus(mav.getStatus());
        mavContainer.addAllAttributes(mav.getModel());
    }




<div class="se-preview-section-delimiter"></div>



6. 執行攔截器的postHandle(HttpServletRequest, HttpServletResponse, Object, ModelAndView)方法
執行攔截器的這個方法是要正常運行完HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)方法,如果中間拋出異常或發生錯誤(error),這個方法不被執行,但是攔截器的afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)方法依然會執行。
需要注意的是如果HandlerMethod對應的的Controller方法中定義了@ResponseBody的話,ModelAndView將等於null.


7. 處理異常和渲染頁面
到這一步執行的方法是processDispatchResult(HttpServletRequest, HttpServletResponse,HandlerExecutionChain, ModelAndView, Exception)

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) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                //處理Handler拋出的異常
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }

        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            //數據渲染
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                        "': assuming HandlerAdapter completed request handling");
            }
        }

        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }

        //執行攔截器的afterCompletion()方法。
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }




<div class="se-preview-section-delimiter"></div>
  • 異常處理
    在這個方法中會先去判斷在HandlerAdapter#handler(HttpServletRequest, HttpServletResponse)方法中是否出現異常或錯誤,如果有出現,則調用xml配置文件中的HandlerExceptionResolver#resolveException(HttpServletRequest, HttpServletResponse, Object, Exception)方法,這個方法會返回一個ModelAndView對象,在配置文件中可以配多個HandlerExceptionResolver,如果HandlerExceptionResolver#resolveException(HttpServletRequest, HttpServletResponse, Object, Exception)方法返回值等null,將會繼續執行下一個HandlerExceptionResolver#resolveException(HttpServletRequest, HttpServletResponse, Object, Exception)方法。所以在配置HandlerExceptionResolver時也需要注意。
    代碼如下:
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) throws Exception {

        // Check registered HandlerExceptionResolvers...
        ModelAndView exMv = null;
        for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
            exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }
        if (exMv != null) {
            if (exMv.isEmpty()) {
                request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
                return null;
            }
            // We might still need view name translation for a plain error model...
            if (!exMv.hasView()) {
                exMv.setViewName(getDefaultViewName(request));
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
            }
            WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
            return exMv;
        }

        throw ex;
    }




<div class="se-preview-section-delimiter"></div>
  • 視圖渲染
    如果ModelAndView對象不等於null,會通過ViewResolver對象解析出相應的View對象,然後由View對象去渲染頁面。

DispatcherServlet

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Determine locale for request and apply it to the response.
        Locale locale = this.localeResolver.resolveLocale(request);
        response.setLocale(locale);

        View view;
        //是否引用,mv.isReference()的代碼如下
        /*public boolean isReference() {
        return (this.view instanceof String);
    }*/
        if (mv.isReference()) {
            // We need to resolve the view name.
            //調用ViewResolver解析視圖
            view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                        "' in servlet with name '" + getServletName() + "'");
            }
        }
        else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                        "View object in servlet with name '" + getServletName() + "'");
            }
        }

        // Delegate to the View object for rendering.
        if (logger.isDebugEnabled()) {
            logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            //渲染
            view.render(mv.getModelInternal(), request, response);
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                        getServletName() + "'", ex);
            }
            throw ex;
        }
    }




<div class="se-preview-section-delimiter"></div>

ViewResolver可以在xml配置文件中配置多個,一般ViewResolver的實現類都實現Ordered接口,所以可以通過Ordered排序,只要ViewResolver解析到View,就返回View去渲染。

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
            HttpServletRequest request) throws Exception {

        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
        return null;
    }




<div class="se-preview-section-delimiter"></div>

以FreeMarkerViewResolver爲例,創建和加載View對象的方法分別在父類AbstractCachingViewResolver和UrlBasedViewResolver中,由FreeMarkerView去檢查模版是否存在,如果不存在返回null,如果存在就返回,然後渲染頁面。

UrlBasedViewResolver


protected View createView(String viewName, Locale locale) throws Exception {
        // If this resolver is not supposed to handle the given view,
        // return null to pass on to the next resolver in the chain.
        if (!canHandle(viewName, locale)) {
            return null;
        }
        // Check for special "redirect:" prefix.
        if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
            String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
            RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
            view.setHosts(getRedirectHosts());
            return applyLifecycleMethods(viewName, view);
        }
        // Check for special "forward:" prefix.
        if (viewName.startsWith(FORWARD_URL_PREFIX)) {
            String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
            return new InternalResourceView(forwardUrl);
        }
        // Else fall back to superclass implementation: calling loadView.
        //調用父類的方法
        return super.createView(viewName, locale);
    }

//實現父類的抽象方法
protected View loadView(String viewName, Locale locale) throws Exception {
        AbstractUrlBasedView view = buildView(viewName);
        View result = applyLifecycleMethods(viewName, view);
        return (view.checkResource(locale) ? result : null);
    }

    //創建View
    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
        view.setUrl(getPrefix() + viewName + getSuffix());

        String contentType = getContentType();
        if (contentType != null) {
            view.setContentType(contentType);
        }

        view.setRequestContextAttribute(getRequestContextAttribute());
        view.setAttributesMap(getAttributesMap());

        Boolean exposePathVariables = getExposePathVariables();
        if (exposePathVariables != null) {
            view.setExposePathVariables(exposePathVariables);
        }
        Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
        if (exposeContextBeansAsAttributes != null) {
            view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
        }
        String[] exposedContextBeanNames = getExposedContextBeanNames();
        if (exposedContextBeanNames != null) {
            view.setExposedContextBeanNames(exposedContextBeanNames);
        }

        return view;
    }

    //設置View的Class
    public void setViewClass(Class<?> viewClass) {
        if (viewClass == null || !requiredViewClass().isAssignableFrom(viewClass)) {
            throw new IllegalArgumentException(
                    "Given view class [" + (viewClass != null ? viewClass.getName() : null) +
                    "] is not of type [" + requiredViewClass().getName() + "]");
        }
        this.viewClass = viewClass;
    }

    //生成View對象時用到
    protected Class<?> getViewClass() {
        return this.viewClass;
    }


    protected Class<?> requiredViewClass() {
        return AbstractUrlBasedView.class;
    }





<div class="se-preview-section-delimiter"></div>

AbstractCachingViewResolver

public View resolveViewName(String viewName, Locale locale) throws Exception {
        if (!isCache()) {
            return createView(viewName, locale);
        }
        else {
            Object cacheKey = getCacheKey(viewName, locale);
            View view = this.viewAccessCache.get(cacheKey);
            if (view == null) {
                synchronized (this.viewCreationCache) {
                    view = this.viewCreationCache.get(cacheKey);
                    if (view == null) {
                        // Ask the subclass to create the View object.
                        view = createView(viewName, locale);
                        if (view == null && this.cacheUnresolved) {
                            view = UNRESOLVED_VIEW;
                        }
                        if (view != null) {
                            this.viewAccessCache.put(cacheKey, view);
                            this.viewCreationCache.put(cacheKey, view);
                            if (logger.isTraceEnabled()) {
                                logger.trace("Cached view [" + cacheKey + "]");
                            }
                        }
                    }
                }
            }
            return (view != UNRESOLVED_VIEW ? view : null);
        }
    }



    protected View createView(String viewName, Locale locale) throws Exception {
        //調用子類的加載view的方法
        return loadView(viewName, locale);
    }

    //抽象方法
    protected abstract View loadView(String viewName, Locale locale) throws Exception;




<div class="se-preview-section-delimiter"></div>

FreeMarkerViewResolver

public class FreeMarkerViewResolver extends AbstractTemplateViewResolver {

    /**
     * Sets the default {@link #setViewClass view class} to {@link #requiredViewClass}:
     * by default {@link FreeMarkerView}.
     */
    public FreeMarkerViewResolver() {
        //設置創建的View對象的Class
        setViewClass(requiredViewClass());
    }

    /**
     * A convenience constructor that allows for specifying {@link #setPrefix prefix}
     * and {@link #setSuffix suffix} as constructor arguments.
     * @param prefix the prefix that gets prepended to view names when building a URL
     * @param suffix the suffix that gets appended to view names when building a URL
     * @since 4.3
     */
    public FreeMarkerViewResolver(String prefix, String suffix) {
        this();
        setPrefix(prefix);
        setSuffix(suffix);
    }


    /**
     * Requires {@link FreeMarkerView}.
     */
    @Override
    protected Class<?> requiredViewClass() {
        return FreeMarkerView.class;
    }

}




<div class="se-preview-section-delimiter"></div>

FreeMarkerView


public boolean checkResource(Locale locale) throws Exception {
        try {
            // Check that we can get the template, even if we might subsequently get it again.
            getTemplate(getUrl(), locale);
            return true;
        }
        catch (FileNotFoundException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("No FreeMarker view found for URL: " + getUrl());
            }
            return false;
        }
        catch (ParseException ex) {
            throw new ApplicationContextException(
                    "Failed to parse FreeMarker template for URL [" +  getUrl() + "]", ex);
        }
        catch (IOException ex) {
            throw new ApplicationContextException(
                    "Could not load FreeMarker template for URL [" + getUrl() + "]", ex);
        }
    }

    //獲取模版,如果模不存在,會拋異常。
    protected Template getTemplate(String name, Locale locale) throws IOException {
        return (getEncoding() != null ?
                getConfiguration().getTemplate(name, locale, getEncoding()) :
                getConfiguration().getTemplate(name, locale));
    }
  • 執行攔截器的afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)方法
    在最後將會執行攔截器的afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)方法


    這裏要注意,如果攔截器的afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)方法中有拋異常,那麼攔截器的afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)方法將會被執行兩遍。



8. 執行攔截器的afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)方法
攔截器的這個方法不管是否拋出異常都會被執行,如果拋出異常,方法參數中的Exception將不爲空。所以如果要在攔截器中釋放資源,不要攔截器的postHandle(HttpServletRequest, HttpServletResponse, Object, ModelAndView)中釋放,應該在這個方法中釋放。

@RequerstMapping使用示例 http://blog.csdn.net/kobejayandy/article/details/12690041
HandlerMethodArgumentResolver介紹 http://blog.csdn.net/qq_34120041/article/details/53606561

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