你真的了解Spring MVC處理請(qǐng)求流程嗎?

前言

閱讀本文章大概需要8分鐘左右。相信會(huì)讓你對(duì)Spring MVC的理解更加深刻凄鼻,更上一層樓。

SpringMVC圖解

粒度很粗的圖解
自己畫的.png

粒度比較粗的圖解
自己畫的.png

粒度一般的圖解
本圖來(lái)自互聯(lián)網(wǎng).png

FrameworkServlet

本圖來(lái)自互聯(lián)網(wǎng).png

FrameworkServlet是Spring MVC框架中的基本Servlet,集成提供了Spring應(yīng)用的上下文块蚌。通過(guò)讀取我們?cè)趙eb.xml中配置的ContextConfigLocation闰非、ContextLoaderListener、ContextClass屬性注入上下文匈子。子類必須重寫doService()方法去處理請(qǐng)求河胎。

假如我們要請(qǐng)求http://localhost:8081/order/detail?orderId=1,由于我們的請(qǐng)求方式是GET,會(huì)進(jìn)入到doGet()方法虎敦。實(shí)際上這個(gè)方法會(huì)把請(qǐng)求委托給processRequest()和doService()處理游岳。

    /**
     * Delegate GET requests to processRequest/doService.
     * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
     * with a {@code NoBodyResponse} that just captures the content length.
     * @see #doService
     * @see #doHead
     */
    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

在processRequest()方法中,會(huì)處理這個(gè)請(qǐng)求其徙,并且不管結(jié)果如何胚迫,都會(huì)發(fā)布一個(gè)請(qǐng)求事件。實(shí)際上處理請(qǐng)求是子類DispatcherServlet的doService()方法完成的唾那。

    /**
     * Process this request, publishing an event regardless of the outcome.
     * <p>The actual event handling is performed by the abstract
     * {@link #doService} template method.
     */
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;

        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = buildLocaleContext(request);

        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

        initContextHolders(request, localeContext, requestAttributes);

        try {
            doService(request, response);
        }
        catch (ServletException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }

        finally {
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            if (logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", failureCause);
                }
                else {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        logger.debug("Leaving response open for concurrent processing");
                    }
                    else {
                        this.logger.debug("Successfully completed request");
                    }
                }
            }

            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

DispatcherServlet

DispatcherServlet就是一個(gè)前端控制器访锻,集中提供請(qǐng)求處理機(jī)制。將url映射到指定的Controller處理闹获,Controller處理完畢后將ModelAndView返回給DispatcherServlet期犬,DispatcherServlet通過(guò)viewResovler進(jìn)行視圖解析,然后將model填充到view避诽,響應(yīng)給用戶龟虎。

doService()方法會(huì)將判斷該請(qǐng)求是不是包含請(qǐng)求。如果是包含請(qǐng)求沙庐,會(huì)將request對(duì)象的參數(shù)進(jìn)行快照鲤妥,以便在包含后恢復(fù)這些屬性。這些屬性分別是

javax.servlet.include.request_uri
javax.servlet.include.context_path
javax.servlet.include.servlet_path
javax.servlet.include.path_info
javax.servlet.include.query_string

接著將Spring MVC框架的全局對(duì)象注入到request對(duì)象中拱雏,讓handler和view對(duì)象可用棉安。接著調(diào)用doDispatch()方法

    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(DEFAULT_STRATEGIES_PREFIX)) {
                    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);
                }
            }
        }
    }

doDispatch()這個(gè)方法很核心,把請(qǐng)求調(diào)度給真正的handler去處理铸抑。

    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 {
                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);
                }
            }
        }
    }

checkMultipart(request)判斷這個(gè)請(qǐng)求是否是Multipart贡耽,比如文件上傳就是Multipart請(qǐng)求。如果是Multipart請(qǐng)求就交給multipartResolver處理鹊汛,如果不是Multipart返回當(dāng)前的請(qǐng)求蒲赂。

    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 (hasMultipartException(request) ) {
                logger.debug("Multipart resolution failed for current request before - " +
                        "skipping re-resolution for undisturbed error rendering");
            }
            else {
                try {
                    return this.multipartResolver.resolveMultipart(request);
                }
                catch (MultipartException ex) {
                    if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
                        logger.debug("Multipart resolution failed for error dispatch", ex);
                        // Keep processing error dispatch with regular request handle below
                    }
                    else {
                        throw ex;
                    }
                }
            }
        }
        // If not returned before: return original request.
        return request;
    }

multipartRequestParsed = (processedRequest != request)判斷處理后的請(qǐng)求是否和處理前的請(qǐng)求一致。如果不一致柒昏,multipartRequestParsed標(biāo)志為true凳宙,代表這個(gè)請(qǐng)求已經(jīng)被multipartResolver處理過(guò)了。

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);

在getHandler(processedRequest)里面通過(guò)遍歷所有的handlerMapping职祷,調(diào)用handlerMapping對(duì)象中的getHandler(request)方法獲得HandlerExecutionChain對(duì)象氏涩。實(shí)際上這里的handlerMapping對(duì)象是RequestMappingHandlerMapping對(duì)象届囚。

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

進(jìn)入到AbstractHandlerMapping中的getHandler(request),一看究竟是尖。handler是通過(guò)getHandlerInternal(request)獲得的意系。

    /**
     * Look up a handler for the given request, falling back to the default
     * handler if no specific one is found.
     * @param request current HTTP request
     * @return the corresponding handler instance, or the default handler
     * @see #getHandlerInternal
     */
    @Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }

        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }

進(jìn)入到AbstractHandlerMethodMapping中的getHandlerInternal(request)方法,先從request對(duì)象獲取當(dāng)前要查詢的lookupPath饺汹。

    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        if (logger.isDebugEnabled()) {
            logger.debug("Looking up handler method for path " + lookupPath);
        }
        this.mappingRegistry.acquireReadLock();
        try {
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            if (logger.isDebugEnabled()) {
                if (handlerMethod != null) {
                    logger.debug("Returning handler method [" + handlerMethod + "]");
                }
                else {
                    logger.debug("Did not find handler method for [" + lookupPath + "]");
                }
            }
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }

這里的mappingRegistry說(shuō)白了就是一個(gè)映射關(guān)系注冊(cè)中心蛔添,里面維護(hù)了所有mapping到處理程序handlerMethod的映射關(guān)系,以便查找和提供并發(fā)訪問(wèn)兜辞。所以每次通過(guò)訪問(wèn)顯式獲得鎖迎瞧,訪問(wèn)結(jié)束后要顯式釋放鎖。

    /**
     * A registry that maintains all mappings to handler methods, exposing methods
     * to perform lookups and providing concurrent access.
     *
     * <p>Package-private for testing purposes.
     */
    class MappingRegistry {

        private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();

        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();

        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();

        private final Map<String, List<HandlerMethod>> nameLookup =
                new ConcurrentHashMap<String, List<HandlerMethod>>();

        private final Map<HandlerMethod, CorsConfiguration> corsLookup =
                new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();

        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        /**
         * Return all mappings and handler methods. Not thread-safe.
         * @see #acquireReadLock()
         */
        public Map<T, HandlerMethod> getMappings() {
            return this.mappingLookup;
        }

        /**
         * Return matches for the given URL path. Not thread-safe.
         * @see #acquireReadLock()
         */
        public List<T> getMappingsByUrl(String urlPath) {
            return this.urlLookup.get(urlPath);
        }

        /**
         * Return handler methods by mapping name. Thread-safe for concurrent use.
         */
        public List<HandlerMethod> getHandlerMethodsByMappingName(String mappingName) {
            return this.nameLookup.get(mappingName);
        }

        /**
         * Return CORS configuration. Thread-safe for concurrent use.
         */
        public CorsConfiguration getCorsConfiguration(HandlerMethod handlerMethod) {
            HandlerMethod original = handlerMethod.getResolvedFromHandlerMethod();
            return this.corsLookup.get(original != null ? original : handlerMethod);
        }

        /**
         * Acquire the read lock when using getMappings and getMappingsByUrl.
         */
        public void acquireReadLock() {
            this.readWriteLock.readLock().lock();
        }

        /**
         * Release the read lock after using getMappings and getMappingsByUrl.
         */
        public void releaseReadLock() {
            this.readWriteLock.readLock().unlock();
        }

        public void register(T mapping, Object handler, Method method) {
            this.readWriteLock.writeLock().lock();
            try {
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                assertUniqueMethodMapping(handlerMethod, mapping);

                if (logger.isInfoEnabled()) {
                    logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
                }
                this.mappingLookup.put(mapping, handlerMethod);

                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                String name = null;
                if (getNamingStrategy() != null) {
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    addMappingName(name, handlerMethod);
                }

                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

        private void assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping) {
            HandlerMethod handlerMethod = this.mappingLookup.get(mapping);
            if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) {
                throw new IllegalStateException(
                        "Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method \n" +
                        newHandlerMethod + "\nto " + mapping + ": There is already '" +
                        handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");
            }
        }

        private List<String> getDirectUrls(T mapping) {
            List<String> urls = new ArrayList<String>(1);
            for (String path : getMappingPathPatterns(mapping)) {
                if (!getPathMatcher().isPattern(path)) {
                    urls.add(path);
                }
            }
            return urls;
        }

        private void addMappingName(String name, HandlerMethod handlerMethod) {
            List<HandlerMethod> oldList = this.nameLookup.get(name);
            if (oldList == null) {
                oldList = Collections.<HandlerMethod>emptyList();
            }

            for (HandlerMethod current : oldList) {
                if (handlerMethod.equals(current)) {
                    return;
                }
            }

            if (logger.isTraceEnabled()) {
                logger.trace("Mapping name '" + name + "'");
            }

            List<HandlerMethod> newList = new ArrayList<HandlerMethod>(oldList.size() + 1);
            newList.addAll(oldList);
            newList.add(handlerMethod);
            this.nameLookup.put(name, newList);

            if (newList.size() > 1) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Mapping name clash for handlerMethods " + newList +
                            ". Consider assigning explicit names.");
                }
            }
        }

        public void unregister(T mapping) {
            this.readWriteLock.writeLock().lock();
            try {
                MappingRegistration<T> definition = this.registry.remove(mapping);
                if (definition == null) {
                    return;
                }

                this.mappingLookup.remove(definition.getMapping());

                for (String url : definition.getDirectUrls()) {
                    List<T> list = this.urlLookup.get(url);
                    if (list != null) {
                        list.remove(definition.getMapping());
                        if (list.isEmpty()) {
                            this.urlLookup.remove(url);
                        }
                    }
                }

                removeMappingName(definition);

                this.corsLookup.remove(definition.getHandlerMethod());
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

        private void removeMappingName(MappingRegistration<T> definition) {
            String name = definition.getMappingName();
            if (name == null) {
                return;
            }
            HandlerMethod handlerMethod = definition.getHandlerMethod();
            List<HandlerMethod> oldList = this.nameLookup.get(name);
            if (oldList == null) {
                return;
            }
            if (oldList.size() <= 1) {
                this.nameLookup.remove(name);
                return;
            }
            List<HandlerMethod> newList = new ArrayList<HandlerMethod>(oldList.size() - 1);
            for (HandlerMethod current : oldList) {
                if (!current.equals(handlerMethod)) {
                    newList.add(current);
                }
            }
            this.nameLookup.put(name, newList);
        }
    }

我們繼續(xù)回到AbstractHandlerMethodMapping中的getHandlerInternal(request)方法中逸吵,通過(guò)調(diào)用this.mappingRegistry.acquireReadLock()方法申請(qǐng)獲得mapping注冊(cè)中心中的讀鎖凶硅。

this.mappingRegistry.acquireReadLock();
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);

接著調(diào)用lookupHandlerMethod(lookupPath, request),通過(guò)url匹配的方式獲得合適的hanlderMethod扫皱。

    /**
     * Look up the best-matching handler method for the current request.
     * If multiple matches are found, the best match is selected.
     * @param lookupPath mapping lookup path within the current servlet mapping
     * @param request the current request
     * @return the best-matching handler method, or {@code null} if no match
     * @see #handleMatch(Object, String, HttpServletRequest)
     * @see #handleNoMatch(Set, String, HttpServletRequest)
     */
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<Match>();
                //通過(guò)lookupPath足绅,在this.urlLookup.get(urlPath)獲取List<RequestMappingInfo>集合
        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);
        }
    }

我們可以關(guān)注List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);這個(gè)方法實(shí)際上是從mappingRegistry中的urlLookup獲得List<RequestMappingInfo>集合. urlLookup的結(jié)構(gòu)是Map<K, List<V>>, K是url, List<V>對(duì)應(yīng)著RequestMappingInfo韩脑,實(shí)際上每一個(gè)RequestMapping最后都會(huì)被封裝成RequestMappingInfo.

我們可以看到directPathMatches不為空氢妈,會(huì)調(diào)用addMatchingMappings(directPathMatches, matches, request),我們仔細(xì)關(guān)注T match = getMatchingMapping(mapping, request)這一行代碼段多。

    private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
        for (T mapping : mappings) {
            T match = getMatchingMapping(mapping, request);
            if (match != null) {
                matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
            }
        }
    }

當(dāng)前請(qǐng)求的url和RequestMappingInfo中的PatternsRequestCondition對(duì)象中的url集合中是否匹配首量,如果匹配成功,返回一個(gè)新的RequestMappingInfo衩匣。

    /**
     * Checks if all conditions in this request mapping info match the provided request and returns
     * a potentially new request mapping info with conditions tailored to the current request.
     * <p>For example the returned instance may contain the subset of URL patterns that match to
     * the current request, sorted with best matching patterns on top.
     * @return a new instance in case all conditions match; or {@code null} otherwise
     */
    @Override
    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
        RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
        ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
        HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
        ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
        ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);

        if (methods == null || params == null || headers == null || consumes == null || produces == null) {
            return null;
        }

        PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
        if (patterns == null) {
            return null;
        }

        RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
        if (custom == null) {
            return null;
        }

        return new RequestMappingInfo(this.name, patterns,
                methods, params, headers, consumes, produces, custom.getCondition());
    }

接著關(guān)注matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));這一行代碼蕾总,Match是AbstractHandlerMethodMapping的內(nèi)部類粥航,this.mappingRegistry.getMappings()是獲取映射注冊(cè)中心的mappingLookup對(duì)象琅捏,其結(jié)構(gòu)為Map<RequestMappingInfo, HandlerMethod>。

    /**
     * A thin wrapper around a matched HandlerMethod and its mapping, for the purpose of
     * comparing the best match with a comparator in the context of the current request.
     */
    private class Match {

        private final T mapping;

        private final HandlerMethod handlerMethod;

        public Match(T mapping, HandlerMethod handlerMethod) {
            this.mapping = mapping;
            this.handlerMethod = handlerMethod;
        }

        @Override
        public String toString() {
            return this.mapping.toString();
        }
    }

回到AbstractHandlerMethodMapping中的lookupHandlerMethod(String lookupPath, HttpServletRequest request)方法递雀,如果matches為空柄延,則遍歷mappingRegistry中的mappingLookup集合,并且填充到matches缀程。最后通過(guò)排序比較搜吧,獲得matches集合中的第一個(gè)Match對(duì)象,此對(duì)象也是最匹配的杨凑,返回Match對(duì)象中的handlerMethod滤奈。

        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

回到AbstractHandlerMethodMapping中的getHandlerInternal(HttpServletRequest request)方法,如果獲得的handlerMethod不為空撩满,調(diào)用createWithResolvedBean()方法蜒程。其中的邏輯是如果當(dāng)前handlerMethod中的bean只是bean的名稱而不是真正的bean實(shí)例時(shí)绅你,那么通過(guò)名稱獲得bean的實(shí)例。并且返回一個(gè)新的HandlerMethod昭躺。這里的bean是handlerMethd所屬于的類忌锯。比如UserController中有一個(gè)login()方法,bean就是UserController领炫,login就是HandlerMethod偶垮。最后釋放mappingRegistry的讀鎖。

return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    /**
     * If the provided instance contains a bean name rather than an object instance,
     * the bean name is resolved before a {@link HandlerMethod} is created and returned.
     */
    public HandlerMethod createWithResolvedBean() {
        Object handler = this.bean;
        if (this.bean instanceof String) {
            String beanName = (String) this.bean;
            handler = this.beanFactory.getBean(beanName);
        }
        return new HandlerMethod(this, handler);
    }

回到AbstractHandlerMapping中的getHandler(HttpServletRequest request)方法帝洪,接著調(diào)用 getHandlerExecutionChain(handler, request)似舵,遍歷所有的handlerInterceptor,把handler和handlerInterceptor(攔截器)封裝成handlerExecutionChain(處理程序鏈)葱峡。還有一點(diǎn)就是MappedInterceptor里面有includePatterns和excludePatterns屬性啄枕。通過(guò)這2個(gè)屬性,設(shè)置需要被攔截的url和不需要被攔截的url族沃。

    /**
     * Build a {@link HandlerExecutionChain} for the given handler, including
     * applicable interceptors.
     * <p>The default implementation builds a standard {@link HandlerExecutionChain}
     * with the given handler, the handler mapping's common interceptors, and any
     * {@link MappedInterceptor}s matching to the current request URL. Interceptors
     * are added in the order they were registered. Subclasses may override this
     * in order to extend/rearrange the list of interceptors.
     * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a
     * pre-built {@link HandlerExecutionChain}. This method should handle those
     * two cases explicitly, either building a new {@link HandlerExecutionChain}
     * or extending the existing chain.
     * <p>For simply adding an interceptor in a custom subclass, consider calling
     * {@code super.getHandlerExecutionChain(handler, request)} and invoking
     * {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
     * @param handler the resolved handler instance (never {@code null})
     * @param request current HTTP request
     * @return the HandlerExecutionChain (never {@code null})
     * @see #getAdaptedInterceptors()
     */
    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;
    }

返回到AbstractHandlerMapping中的getHandler(request)中频祝,我們已經(jīng)獲取到executionChain對(duì)象,可以返回該對(duì)象脆淹。

HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

返回到DispatcherServlet中的getHandler(HttpServletRequest request)返回當(dāng)前request請(qǐng)求中的executionChain對(duì)象

HandlerExecutionChain handler = hm.getHandler(request);

我們繼續(xù)回到DispatchServlet中的doDispatcher()方法常空,如果當(dāng)前handlerExecutionChain(處理程序執(zhí)行鏈)等于空或者h(yuǎn)andlerExecutionChain中的handlerMethod為空的話,就會(huì)拋出著名的NoHandlerFoundException異常

                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

如果你不信盖溺,可以點(diǎn)開noHandlerFound(processedRequest, response);

    /**
     * No handler found -> set appropriate HTTP response status.
     * @param request current HTTP request
     * @param response current HTTP response
     * @throws Exception if preparing the response failed
     */
    protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (pageNotFoundLogger.isWarnEnabled()) {
            pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
                    "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        if (this.throwExceptionIfNoHandlerFound) {
            throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
                    new ServletServerHttpRequest(request).getHeaders());
        }
        else {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
    }

接著看漓糙,我要從當(dāng)前請(qǐng)求獲取能夠支持當(dāng)前handlerMethod的適配器。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

點(diǎn)開代碼看看烘嘱,看看細(xì)節(jié)昆禽。主要是循環(huán)當(dāng)前所有的handlerAdapters,通過(guò)supports()判斷是否支持當(dāng)前handlerMethod蝇庭,這種循環(huán)比對(duì)思想在Spring MVC源碼隨處可見(jiàn)醉鳖。

    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");
    }

進(jìn)入到AbstractHandlerMethodAdapter中的supports()方法,通過(guò)判斷當(dāng)前handler對(duì)象是否是HandlerMethod類的實(shí)例和是否支持當(dāng)前handlerMethod哮内。

    /**
     * This implementation expects the handler to be an {@link HandlerMethod}.
     * @param handler the handler instance to check
     * @return whether or not this adapter can adapt the given handler
     */
    @Override
    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }

點(diǎn)開盗棵,進(jìn)入到RequestMappingHandlerAdapter中的supportsInternal(),恍然大悟。這個(gè)方法總是返回true北发,因?yàn)槿魏畏椒ǖ膮?shù)和返回值都以某種方式處理

    /**
     * Always return {@code true} since any method argument and return value
     * type will be processed in some way. A method argument not recognized
     * by any HandlerMethodArgumentResolver is interpreted as a request parameter
     * if it is a simple type, or as a model attribute otherwise. A return value
     * not recognized by any HandlerMethodReturnValueHandler will be interpreted
     * as a model attribute.
     */
    @Override
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }

判斷request的請(qǐng)求方式纹因,如果是GET或者是HEAD,用戶當(dāng)前請(qǐng)求上一次請(qǐng)求的時(shí)間戳琳拨,通過(guò)checkNotModified()判斷是否修改過(guò)瞭恰。如果沒(méi)有修改過(guò),返回狀態(tài)碼304狱庇。

                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;
                    }
                }

如果handlerExecutionChain中的攔截器preHandle返回false惊畏,就不會(huì)調(diào)用postHandle()蓝仲,直接清理資源朗涩,然后返回。

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

進(jìn)入HandlerExecutionChain中的applyPreHandle(processedRequest, response)方法。遍歷HandlerExecutionChain中的所有攔截器,如果攔截器中的preHandle(request, response, this.handler)返回false抄淑,那么直接調(diào)用triggerAfterCompletion(request, response, null)進(jìn)行資源清理难捌,返回false殴边。通過(guò)記錄interceptorIndex來(lái)標(biāo)志當(dāng)前執(zhí)行的攔截器厉斟。

        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)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;

triggerAfterCompletion()方法,也是大同小異乳规。遍歷所有攔截器形葬,調(diào)用攔截器中清理資源的方法afterCompletion()。

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
            throws Exception {

        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = this.interceptorIndex; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                }
                catch (Throwable ex2) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                }
            }
        }
    }

小高潮來(lái)了暮的,handlerAdapter(處理程序適配器)開始調(diào)用handlerMethod(處理程序)的功能方法笙以。

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

進(jìn)入到AbstractHandlerMethodAdapter中的handle()方法

    @Override
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return handleInternal(request, response, (HandlerMethod) handler);
    }

進(jìn)入RequestMappingHandlerAdapter中的handleInternal()方法,我們可以仔細(xì)看看這個(gè)方法做了什么冻辩。

    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        checkRequest(request);

        // Execute invokeHandlerMethod in synchronized block if required.
        if (this.synchronizeOnSession) {
                        //獲取session猖腕,如果為空直接返回null
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No HttpSession available -> no mutex necessary
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No synchronization on session demanded at all...
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
        }

        return mav;
    }

通過(guò)調(diào)用WebContentGenerator中的checkRequest()方法,判斷支持的請(qǐng)求方式是否包含當(dāng)前請(qǐng)求的方式恨闪,如果supportedMethods不為空且不支持當(dāng)前請(qǐng)求方式倘感,會(huì)拋出著名的HttpRequestMetohdNotSupportedException。如果需要session且從當(dāng)前請(qǐng)求獲得不到session咙咽,同樣拋出HttpSessionRequiredException異常老玛。

    /**
     * Check the given request for supported methods and a required session, if any.
     * @param request current HTTP request
     * @throws ServletException if the request cannot be handled because a check failed
     * @since 4.2
     */
    protected final void checkRequest(HttpServletRequest request) throws ServletException {
        // Check whether we should support the request method.
        String method = request.getMethod();
        if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
            throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
        }

        // Check whether a session is required.
        if (this.requireSession && request.getSession(false) == null) {
            throw new HttpSessionRequiredException("Pre-existing session required but none found");
        }
    }

通過(guò)synchronizeOnSession標(biāo)識(shí)符,判斷調(diào)用invokeHandlerMethod是否需要同步機(jī)制钧敞。
然后調(diào)用invokeHandlerMethod()

mav = invokeHandlerMethod(request, response, handlerMethod);

進(jìn)入到RequestMappingHandlerAdapter中的invokeHandlerMethod()中蜡豹,首先RequestMappingHandlerAdapter支持{@link #setCustomArgumentResolvers}和{@link #setCustomReturnValueHandlers}配置自定義參數(shù)和自定義返回值,也支持來(lái)配置{@link #setArgumentResolvers}和{@link #setReturnValueHandlers}所有參數(shù)和返回值溉苛。

    /**
     * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
     * if view resolution is required.
     * @since 4.2
     * @see #createInvocableHandlerMethod(HandlerMethod)
     */
    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();
        }
    }

創(chuàng)建WebDataBinderFactory實(shí)例镜廉,用于創(chuàng)建WebDataBinder對(duì)象,用于web參數(shù)綁定炊昆。

WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

比如我們現(xiàn)在有一個(gè)需求桨吊,從前臺(tái)傳來(lái)的日期字符串威根,我們要全部解析成Date類型的凤巨。一般有3種方式解決:PropertyEditor、Formatter洛搀、Converter去解決敢茁。最常見(jiàn)的做法實(shí)現(xiàn)WebBindingInitializer接口,通過(guò)WebDataBinder注冊(cè)屬性編輯器留美。

/**
 * WebBindingInitializer
 */
public class WebBindingInitializer implements org.springframework.web.bind.support.WebBindingInitializer {

    /* (non-Javadoc)
     * @see org.springframework.web.bind.support.WebBindingInitializer#initBinder(org.springframework.web.bind.WebDataBinder, org.springframework.web.context.request.WebRequest)
     */
    @Override
    public void initBinder(WebDataBinder binder, WebRequest request) {
        binder.registerCustomEditor(String.class, new DatePropertyEditor());
    }

}

回到正軌彰檬,看一下是怎么創(chuàng)建WebDataBinderFactory實(shí)例伸刃。

    private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
        Class<?> handlerType = handlerMethod.getBeanType();
        Set<Method> methods = this.initBinderCache.get(handlerType);
        if (methods == null) {
            methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
            this.initBinderCache.put(handlerType, methods);
        }
        List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
        // Global methods first
        for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
            if (entry.getKey().isApplicableToBeanType(handlerType)) {
                Object bean = entry.getKey().resolveBean();
                for (Method method : entry.getValue()) {
                    initBinderMethods.add(createInitBinderMethod(bean, method));
                }
            }
        }
        for (Method method : methods) {
            Object bean = handlerMethod.getBean();
            initBinderMethods.add(createInitBinderMethod(bean, method));
        }
        return createDataBinderFactory(initBinderMethods);
    }

獲得handlerMethod所在類的類型。通過(guò)所在類的類型獲得從initBinderCache緩存中獲得當(dāng)前類所有的方法逢倍。這些方法應(yīng)該是被@InitBinder注解的方法捧颅。

private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<Class<?>, Set<Method>>(64);

如果methods等于空,那么我們?nèi)カ@得當(dāng)前類下被@InitBinder注解的方法较雕,并放入到initBinderCache緩存中碉哑。

        if (methods == null) {
            methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
            this.initBinderCache.put(handlerType, methods);
        }

優(yōu)先遍歷被@ControllerAdvice注解全局類中的方法,再遍歷被@Controller注解的類的方法亮蒋。通過(guò)createInitBinderMethod(bean, method)方法創(chuàng)建InvocableHandlerMethod對(duì)象(用于參數(shù)準(zhǔn)備扣典,準(zhǔn)備當(dāng)中會(huì)用到WebDataBinderFactory創(chuàng)建WebDataBinder實(shí)例進(jìn)行參數(shù)轉(zhuǎn)換解析綁定,方法調(diào)用)慎玖,并且放入到initBinderMethods集合中贮尖。

        // Global methods first
        for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
            if (entry.getKey().isApplicableToBeanType(handlerType)) {
                Object bean = entry.getKey().resolveBean();
                for (Method method : entry.getValue()) {
                    initBinderMethods.add(createInitBinderMethod(bean, method));
                }
            }
        }
        for (Method method : methods) {
            Object bean = handlerMethod.getBean();
            initBinderMethods.add(createInitBinderMethod(bean, method));
        }

創(chuàng)建InvocableHandlerMethod對(duì)象,注入initBinderArgumentResolvers屬性趁怔、parameterNameDiscoverer(屬性名字發(fā)現(xiàn)器)湿硝、DefaultDataBinderFactory實(shí)例。我們發(fā)現(xiàn)要?jiǎng)?chuàng)建一個(gè)DefaultDataBinderFactory必須要傳入webBindingInitializer润努。

    private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
        InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
        binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
        binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
        binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
        return binderMethod;
    }

最后調(diào)用createDataBinderFactory(initBinderMethods)方法創(chuàng)建ServletRequestDataBinderFactory實(shí)例图柏,同樣也要傳入webBindingInitializer。ServletRequestDataBinderFactory是InitBinderDataBinderFactory的子類任连。

    createDataBinderFactory(initBinderMethods);

    protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
            throws Exception {

        return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
    }

接著創(chuàng)建出ModelFactory實(shí)例蚤吹,我們首先要搞清楚ModelFactory是干啥的。ModelFactory作用是在控制器方法調(diào)用前初始化Model模型随抠,調(diào)用后對(duì)Model模型進(jìn)行更新裁着。在初始化時(shí),通過(guò)調(diào)用被@ModelAttribute注解的方法拱她,Model模型會(huì)在會(huì)話中被臨時(shí)存儲(chǔ)的屬性填充二驰。

ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

我們?cè)賮?lái)看是如何創(chuàng)建ModelFactory實(shí)例的,其實(shí)和創(chuàng)建WebDataBinderFactory的邏輯差不多秉沼。首先獲取sessionAttributesHandler對(duì)象,同樣這個(gè)對(duì)象是從sessionAttributesHandlerCache緩存獲得到的桶雀。key是handlerMethod所在類的類型。如果sessionAttributesHandler沒(méi)有從緩存中獲取到唬复,那么鎖住緩存矗积,再?gòu)木彺嬷腥∫槐椤H绻鹲essionAttributesHandler還為空的話敞咧,那么自己通過(guò)new SessionAttributesHandler(handlerType, sessionAttributeStore)創(chuàng)建一個(gè)默認(rèn)的sessionAttributesHandler對(duì)象棘捣,并放入到緩存中。這種思想是享元設(shè)計(jì)模式休建。

    /**
     * Return the {@link SessionAttributesHandler} instance for the given handler type
     * (never {@code null}).
     */
    private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
        Class<?> handlerType = handlerMethod.getBeanType();
        SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
        if (sessionAttrHandler == null) {
            synchronized (this.sessionAttributesHandlerCache) {
                sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
                if (sessionAttrHandler == null) {
                    sessionAttrHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
                    this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler);
                }
            }
        }
        return sessionAttrHandler;
    }

創(chuàng)建SessionAttributesHandler過(guò)程

    /**
     * Create a new instance for a controller type. Session attribute names and
     * types are extracted from the {@code @SessionAttributes} annotation, if
     * present, on the given type.
     * @param handlerType the controller type
     * @param sessionAttributeStore used for session access
     */
    public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
        Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
        this.sessionAttributeStore = sessionAttributeStore;

        SessionAttributes annotation =
                AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
        if (annotation != null) {
            this.attributeNames.addAll(Arrays.asList(annotation.names()));
            this.attributeTypes.addAll(Arrays.asList(annotation.types()));
        }
        this.knownAttributeNames.addAll(this.attributeNames);
    }

接著通過(guò)modelAttributeCache中獲取handlerMethod所在類中所有被@ModelAttribute注解且沒(méi)有被@RequestMapping注解的方法乍恐。如果沒(méi)有從緩存中查找到评疗,那么通過(guò) MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS)查找,并加入到modelAttributeCache緩存中茵烈。

    private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
        SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
        Class<?> handlerType = handlerMethod.getBeanType();
        Set<Method> methods = this.modelAttributeCache.get(handlerType);
        if (methods == null) {
            methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
            this.modelAttributeCache.put(handlerType, methods);
        }
        List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
        // Global methods first
        for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
            if (entry.getKey().isApplicableToBeanType(handlerType)) {
                Object bean = entry.getKey().resolveBean();
                for (Method method : entry.getValue()) {
                    attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
                }
            }
        }
        for (Method method : methods) {
            Object bean = handlerMethod.getBean();
            attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
        }
        return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
    }

接著老操作百匆,通過(guò)createModelAttributeMethod(binderFactory, bean, method)方法創(chuàng)建InvocableHandlerMethod對(duì)象,并放入到attrMethods集合中呜投。

    private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
        InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
        attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
        attrMethod.setDataBinderFactory(factory);
        return attrMethod;
    }

終于到了new ModelFactory()這一步胧华。通過(guò)handlerMethods集合、WebDataBinderFactory實(shí)例宙彪,SessionAttributesHandler實(shí)例創(chuàng)建出ModelFactory實(shí)例矩动。

    public ModelFactory(List<InvocableHandlerMethod> handlerMethods,
            WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {

        if (handlerMethods != null) {
            for (InvocableHandlerMethod handlerMethod : handlerMethods) {
                this.modelMethods.add(new ModelMethod(handlerMethod));
            }
        }
        this.dataBinderFactory = binderFactory;
        this.sessionAttributesHandler = attributeHandler;
    }

既然binderFactory、modelFactory都被我們?cè)斐鰜?lái)了释漆,那肯定要干正緊事情了悲没。對(duì)handlerMethod進(jìn)行下一步包裝,填充argumentResolvers(HandlerMethodArgumentResolverComposite)男图、returnValueHandlers(HandlerMethodReturnValueHandlerComposite)示姿、binderFactory、parameterNameDiscoverer屬性包裝成ServletInvocableHandlerMethod逊笆。ServletInvocableHandlerMethod的作用對(duì)處理程序的返回值進(jìn)行處理和ResponseStatus處理栈戳。

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

實(shí)例化ModelAndViewContainer容器,把request里面的屬性名為"org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP"的重定向參數(shù)注入到容器中的model模型中难裆。FlashMap的作用是在redirect中傳遞參數(shù)子檀。重定向是會(huì)生成新的request,那么傳遞參數(shù)就不能直接用request進(jìn)行傳遞乃戈。

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

我們關(guān)注到initModel(webRequest, mavCOntainer, invocableMethod)這一行褂痰,它到底干了什么事情。首先從request中獲取檢索@SessionAttribute中名稱的屬性症虑,以Map<String, Object>的結(jié)構(gòu)存儲(chǔ)起來(lái)缩歪,并且放入到ModelAndViewContainer容器的model中。

    public void initModel(NativeWebRequest request, ModelAndViewContainer container,
            HandlerMethod handlerMethod) throws Exception {

        Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
        container.mergeAttributes(sessionAttributes);
        invokeModelAttributeMethods(request, container);

        for (String name : findSessionAttributeArguments(handlerMethod)) {
            if (!container.containsAttribute(name)) {
                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
                if (value == null) {
                    throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
                }
                container.addAttribute(name, value);
            }
        }
    }

接著調(diào)用invokeModelAttributeMethods(request, container)方法谍憔,將被@ModelAttribute注解的handlerMethod中的模型填充到ModelAndViewContainer容器中的model匪蝙。只有當(dāng)容器中不包含當(dāng)前@ModelAtrribute中的屬性時(shí)才添加該屬性至容器。同時(shí)還要判斷當(dāng)前@ModelAttribute中的屬性能不能添加到容器中习贫,如果不能逛球,那么放到容器中的bindingDisabledAttributes進(jìn)行標(biāo)記。然后提前調(diào)用被@ModelAttribute注解的handlerMethod沈条,只有handlerMethod的返回值類型不是void需忿,才能將進(jìn)行數(shù)據(jù)綁定(也就是綁定到容器中的model里)。如果handlerMethod的返回類型不是void蜡歹,那太好了可以進(jìn)行數(shù)據(jù)綁定屋厘。數(shù)據(jù)綁定的規(guī)則是如果@ModelAttribute注解設(shè)置value和name屬性了,優(yōu)先選擇value和name屬性作為model中的key月而,如果沒(méi)有設(shè)置value和name屬性汗洒,選擇被@ModelAttribute注解的handlerMethod的返回類型名稱(首字母小寫)作為model中的key。

    private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
            throws Exception {

        while (!this.modelMethods.isEmpty()) {
            InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
            ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
            if (container.containsAttribute(ann.name())) {
                if (!ann.binding()) {
                    container.setBindingDisabled(ann.name());
                }
                continue;
            }

            Object returnValue = modelMethod.invokeForRequest(request, container);
            if (!modelMethod.isVoid()){
                String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
                if (!ann.binding()) {
                    container.setBindingDisabled(returnValueName);
                }
                if (!container.containsAttribute(returnValueName)) {
                    container.addAttribute(returnValueName, returnValue);
                }
            }
        }
    }

說(shuō)的通俗點(diǎn)父款,如果被@ModelAttribute注解的handlerMethod返回類型是Collection或者是數(shù)組類型溢谤,那么填充到model中的key就是方法返回類型名稱(首字母小寫)再拼接上List『┰埽看下面例子世杀,key就是stringList

    @ModelAttribute
    public List<String> baseTest1() {
        List<String> list = new ArrayList<>();
        list.add("1");
        return list;
    }

    @ModelAttribute
    public String[] baseTest2() {
        String[] strings = new String[1];
        strings[0] = "1";
        return strings;
    }

如果返回類型是String或者是Map,那么key就是string肝集、map

    @ModelAttribute
    public String baseTest() {
        return "1";
    }

    @ModelAttribute
    public Map<String, String> baseTest3() {
        Map<String, String> map = new HashMap<>();
        map.put("username", "password");
        return map;
    }

為什么會(huì)是這樣呢瞻坝,可以看到ModelFactory中的getNameForReturnType(),首先判斷@ModelAttribute注解value屬性是不是為空杏瞻。如果不為空所刀,取value屬性的值。如果為空捞挥,進(jìn)行Conventions.getVariableNameForReturnType(method, resolvedType, returnValue)操作浮创。

    public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) {
        ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
        if (ann != null && StringUtils.hasText(ann.value())) {
            return ann.value();
        }
        else {
            Method method = returnType.getMethod();
            Class<?> containingClass = returnType.getContainingClass();
            Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
            return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
        }
    }

看到getVariableNameForReturnType(),心中的疑惑應(yīng)該解開了把砌函。如果返回類型是Object類型斩披,我們會(huì)通過(guò)返回值來(lái)得出它實(shí)際返回類型,再通過(guò)實(shí)際返回類型推出所在類的簡(jiǎn)稱讹俊,再進(jìn)行格式化返回其短名稱(也就是首字母小寫雏掠,也可以說(shuō)小駝峰)。如果返回類型是Array類型或者是Collection類型劣像,就在其返回基礎(chǔ)上再拼接"List"字符串乡话。

    public static String getVariableNameForReturnType(Method method, Class<?> resolvedType, Object value) {
        Assert.notNull(method, "Method must not be null");

        if (Object.class == resolvedType) {
            if (value == null) {
                throw new IllegalArgumentException("Cannot generate variable name for an Object return type with null value");
            }
            return getVariableName(value);
        }

        Class<?> valueClass;
        boolean pluralize = false;

        if (resolvedType.isArray()) {
            valueClass = resolvedType.getComponentType();
            pluralize = true;
        }
        else if (Collection.class.isAssignableFrom(resolvedType)) {
            valueClass = ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric();
            if (valueClass == null) {
                if (!(value instanceof Collection)) {
                    throw new IllegalArgumentException(
                            "Cannot generate variable name for non-typed Collection return type and a non-Collection value");
                }
                Collection<?> collection = (Collection<?>) value;
                if (collection.isEmpty()) {
                    throw new IllegalArgumentException(
                            "Cannot generate variable name for non-typed Collection return type and an empty Collection value");
                }
                Object valueToCheck = peekAhead(collection);
                valueClass = getClassForValue(valueToCheck);
            }
            pluralize = true;
        }
        else {
            valueClass = resolvedType;
        }

        String name = ClassUtils.getShortNameAsProperty(valueClass);
        return (pluralize ? pluralize(name) : name);
    }

    private static final String PLURAL_SUFFIX = "List";

    private static String pluralize(String name) {
        return name + PLURAL_SUFFIX;
    }

回到ModelFactory中的initModel()上,把目光集中到下面這行代碼上耳奕。

        for (String name : findSessionAttributeArguments(handlerMethod)) {
            if (!container.containsAttribute(name)) {
                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
                if (value == null) {
                    throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
                }
                container.addAttribute(name, value);
            }
        }

遍歷HandlerMethod的參數(shù)绑青,判斷參數(shù)是否被@ModelAttribute注解,如果有屋群,繼續(xù)判斷這個(gè)參數(shù)和參數(shù)類型是否和當(dāng)前handlerMethod所在類中的@SessionAttributes注解中的參數(shù)和類型是否保持一致闸婴。

    /**
     * Find {@code @ModelAttribute} arguments also listed as {@code @SessionAttributes}.
     */
    private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
        List<String> result = new ArrayList<String>();
        for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
            if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
                String name = getNameForParameter(parameter);
                Class<?> paramType = parameter.getParameterType();
                if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
                    result.add(name);
                }
            }
        }
        return result;
    }
    /**
     * Whether the attribute name or type match the names and types specified
     * via {@code @SessionAttributes} on the underlying controller.
     * <p>Attributes successfully resolved through this method are "remembered"
     * and subsequently used in {@link #retrieveAttributes(WebRequest)} and
     * {@link #cleanupAttributes(WebRequest)}.
     * @param attributeName the attribute name to check
     * @param attributeType the type for the attribute
     */
    public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
        Assert.notNull(attributeName, "Attribute name must not be null");
        if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
            this.knownAttributeNames.add(attributeName);
            return true;
        }
        else {
            return false;
        }
    }

通過(guò)findSessionAttributeArguments(handlerMethod)方法,我們得到了合適的參數(shù)名稱集合芍躏。遍歷這個(gè)集合邪乍,我們要判斷ModelAndViewContainer容器中是否存在相同名稱的參數(shù)。如果不存在,我們從sessionAttributeStore根據(jù)名稱中獲得這個(gè)參數(shù)的值庇楞,最后將參數(shù)綁定到容器中榜配。

        for (String name : findSessionAttributeArguments(handlerMethod)) {
            if (!container.containsAttribute(name)) {
                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
                if (value == null) {
                    throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
                }
                container.addAttribute(name, value);
            }
        }

設(shè)置ModelAndViewContainer容器使用defaultModel(默認(rèn)模型),而不是redirectModel(重定向模型)吕晌。

  
   mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

   //ModelAndViewContainer類中的方法
    public ModelMap getModel() {
        if (useDefaultModel()) {
            return this.defaultModel;
        }
        else {
            if (this.redirectModel == null) {
                this.redirectModel = new ModelMap();
            }
            return this.redirectModel;
        }
    }

    private boolean useDefaultModel() {
        return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
    }

處理一些異步請(qǐng)求蛋褥。

            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去調(diào)用invokeAndHandle這個(gè)方法。invokeAndHandle是ServletInvocableHandlerMethod中的方法睛驳。

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

            return getModelAndView(mavContainer, modelFactory, webRequest);

ServletInvocableMethod中的invokeAndHandle()其實(shí)是間接調(diào)用handlerMethod烙心,然后處理handlerMethod的返回值。

    /**
     * Invoke the method and handle the return value through one of the
     * configured {@link HandlerMethodReturnValueHandler}s.
     * @param webRequest the current request
     * @param mavContainer the ModelAndViewContainer for this request
     * @param providedArgs "given" arguments matched by type (not resolved)
     */
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        try {
            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;
        }
    }

我們可以看到invokeForRequest(webRequest, mavContainer, providedArgs)這個(gè)方法會(huì)返回handlerMethod的返回值乏沸。這個(gè)方法在給定請(qǐng)求的上下文中解析handlerMethod的方法參數(shù)后淫茵,然后去調(diào)用handlerMethod。參數(shù)的解析是通過(guò) {@link HandlerMethodArgumentResolver}完成的蹬跃。

    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));
        }
        Object returnValue = doInvoke(args);
        if (logger.isTraceEnabled()) {
            logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                    "] returned [" + returnValue + "]");
        }
        return returnValue;
    }

InvocableHandlerMethod中的getMethodArgumentValues()這個(gè)方法是獲取handlerMethod的參數(shù)匙瘪。首先獲取handlerMethod中的所有參數(shù)數(shù)組,數(shù)組類型是MethodParameter炬转。遍歷參數(shù)數(shù)組辆苔,給每一個(gè)參數(shù)初始化parameterNameDisconverer(參數(shù)名稱發(fā)現(xiàn)器)。

    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        MethodParameter[] parameters = getMethodParameters();
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = resolveProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
            if (this.argumentResolvers.supportsParameter(parameter)) {
                try {
                    args[i] = this.argumentResolvers.resolveArgument(
                            parameter, mavContainer, request, this.dataBinderFactory);
                    continue;
                }
                catch (Exception ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
                    }
                    throw ex;
                }
            }
            if (args[i] == null) {
                throw new IllegalStateException("Could not resolve method parameter at index " +
                        parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
                        ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
            }
        }
        return args;
    }

看到resolveProvidedArgument(parameter, providedArgs)這行代碼扼劈,里面會(huì)對(duì)提供的providedArgs參數(shù)進(jìn)行類型判斷驻啤,判斷它是否和MethodParameter類型匹配。如果類型匹配荐吵,返回提供的參數(shù)骑冗。如果不匹配婴栽,返回null诈胜。

   args[i] = resolveProvidedArgument(parameter, providedArgs);
    /**
     * Attempt to resolve a method parameter from the list of provided argument values.
     */
    private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
        if (providedArgs == null) {
            return null;
        }
        for (Object providedArg : providedArgs) {
            if (parameter.getParameterType().isInstance(providedArg)) {
                return providedArg;
            }
        }
        return null;
    }

接著看,如果提供的參數(shù)值不為空璃赡,那么跳出當(dāng)前循環(huán)薯蝎,繼續(xù)下一次循環(huán)遥倦。

            args[i] = resolveProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }

我們通過(guò)supportParameter()方法判斷argumentResolvers(這是HandlerMethodArgumentResovlerComposite對(duì)象,參數(shù)解析器處理鏈)是否支持parameter這種類型的參數(shù)解析占锯。

            if (this.argumentResolvers.supportsParameter(parameter)) {
                try {
                    args[i] = this.argumentResolvers.resolveArgument(
                            parameter, mavContainer, request, this.dataBinderFactory);
                    continue;
                }
                catch (Exception ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
                    }
                    throw ex;
                }
            }

HandlerMethodArgumentResovlerComposite中的supportsParamter()方法袒哥,通過(guò)parameter參數(shù)類型去獲得合適的HandlerMethodArgumentResolver(參數(shù)解析器)。如果沒(méi)有合適的參數(shù)解析器消略,那么就說(shuō)明HandlerMethodArgumentResolverComposite中沒(méi)有合適的參數(shù)解析器能解析這種類型的參數(shù)堡称。

    /**
     * Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
     * {@link HandlerMethodArgumentResolver}.
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return (getArgumentResolver(parameter) != null);
    }

首先通過(guò)parameter參數(shù)類型從argumentResolverCache緩存中獲得合適的參數(shù)解析器。
如果沒(méi)有找到艺演,那么遍歷HandlerMethodArgumentResolverComposite中所有的參數(shù)器却紧,直到找到能夠解析該parameter類型的參數(shù)解析器為止桐臊,且放入到argumentResolverCache緩存中,緩存的初始容量是256晓殊。

    /**
     * Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
     */
    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;
    }

如果最后的參數(shù)還為空的話断凶,那么很遺憾拋IllegalStateException異常,沒(méi)有合適的參數(shù)解析器能夠解析這個(gè)參數(shù)。

            if (args[i] == null) {
                throw new IllegalStateException("Could not resolve method parameter at index " +
                        parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
                        ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
            }

到了最為關(guān)鍵的一步挺物,開始解析參數(shù)懒浮。

                try {
                    args[i] = this.argumentResolvers.resolveArgument(
                            parameter, mavContainer, request, this.dataBinderFactory);
                    continue;
                }
                catch (Exception ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
                    }
                    throw ex;
                }

進(jìn)入到HandlerMethodArgumentResolverComposite中的resolveArgument()放到飘弧,老樣子從緩存中獲得合適的參數(shù)解析器识藤。并且由這個(gè)參數(shù)解析器來(lái)解析這個(gè)參數(shù)。

    /**
     * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
     * @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
     */
    @Override
    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);
    }

HandlerMethod所需的方法參數(shù)都已經(jīng)解析完畢次伶,那么就可以開始調(diào)用HandlerMethod了痴昧。回到InvocableHandlerMethod中的invokeForRequest()方法冠王。

    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));
        }
        Object returnValue = doInvoke(args);
        if (logger.isTraceEnabled()) {
            logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                    "] returned [" + returnValue + "]");
        }
        return returnValue;
    }

調(diào)用doInvoke(args)方法赶撰,我們可以看到通過(guò)ReflectionUtils.makeAccessible(getBridgedMethod())讓handlerMethod方法具有訪問(wèn)性,必要要顯式的設(shè)置它具有訪問(wèn)性柱彻。然后準(zhǔn)備好handlerMethod所在類的實(shí)例和方法參數(shù)豪娜,反射調(diào)用handlerMethod。

    /**
     * Invoke the handler method with the given argument values.
     */
    protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            return getBridgedMethod().invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
            assertTargetBean(getBridgedMethod(), getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(getInvocationErrorMessage(text, 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 text = getInvocationErrorMessage("Failed to invoke handler method", args);
                throw new IllegalStateException(text, targetException);
            }
        }
    }

handlerMethod調(diào)用完畢后哟楷,可以要對(duì)返回值進(jìn)行處理的操作瘤载。這時(shí)候可以關(guān)注ServletInvocableHandlerMethod中的invokeAndHandle()方法。首先是設(shè)置ResponseStatus的狀態(tài)卖擅,如果有用到{@link ResponseStatus}注解來(lái)設(shè)置響應(yīng)狀態(tài)鸣奔。mavContainer.setRequestHandled(false)只是初始化時(shí)默認(rèn)采用view的解決方案,設(shè)置為true表示response直接處理惩阶,不需要view的解決方案挎狸。

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

        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        try {
            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;
        }
    }

實(shí)際上handlerMethod的返回值處理是通過(guò)HandlerMethodReturnValueHandlerComposite的handleReturnValue()方法,還是老樣子断楷,遍歷所有的返回值處理器锨匆,通過(guò)supportsReturnType()判斷是否支持該返回值的類型。如果類型支持的話冬筒,那么就讓合適的HandlerMethodReturnValueHandler去處理handlerMethod的返回值恐锣。這里的returnType其實(shí)是ReturnValueMethodParameter類型的。(有興趣账千,可以看HandlerMethod這個(gè)類)

   // 調(diào)用HandlerMethodReturnValueHandlerComposite的handleReturnValue()方法
   this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

    /**
     * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
     * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
     */
    @Override
    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;
    }

回到RequestMappingHandlerAdapter中的invokeHandlerMethod()方法中

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

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

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

首先調(diào)用modelFactory.updateModel(webRequest, mavContainer)將被@SessionAtrributes注解的模型屬性上升到會(huì)話級(jí)別侥蒙。如果mavContainer采用的是response直接處理策略,說(shuō)明沒(méi)有采用view的解決方案匀奏,直接返回null即可鞭衩。將ModelAndViewContainer中的model、視圖的名稱、HttpStatus填充到ModelAndView中论衍。如果mavContainer沒(méi)有指定邏輯視圖(或者說(shuō) view不是String類型的)的話瑞佩,那么就設(shè)置視圖對(duì)象。如果model是RedirectAttributes的實(shí)例坯台,那么說(shuō)明是model是重定向所需要的屬性炬丸,我們把model填充到FlashMap即可。

    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
            ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

        modelFactory.updateModel(webRequest, mavContainer);
        if (mavContainer.isRequestHandled()) {
            return null;
        }
        ModelMap model = mavContainer.getModel();
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
        if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
        }
        if (model instanceof RedirectAttributes) {
            Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
        return mav;
    }

這里的updateModel()首先獲得defaultModel蜒蕾,然后判斷當(dāng)前會(huì)話是否處理完畢稠炬。如果處理完畢,進(jìn)行資源清理操作咪啡。如果沒(méi)有處理完畢首启,把當(dāng)前request中的model對(duì)象保存在SessionAttributesHandler中的sessionAttributeStore中,方便下次請(qǐng)求撤摸。如果container采用的是view策略并且使用的是默認(rèn)model模型毅桃,那么就調(diào)用updateBindingResult(request, defaultModel)方法,為需要它的屬性添加到BindingResult屬性到defaultModel中准夷。

    /**
     * Promote model attributes listed as {@code @SessionAttributes} to the session.
     * Add {@link BindingResult} attributes where necessary.
     * @param request the current request
     * @param container contains the model to update
     * @throws Exception if creating BindingResult attributes fails
     */
    public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
        ModelMap defaultModel = container.getDefaultModel();
        if (container.getSessionStatus().isComplete()){
            this.sessionAttributesHandler.cleanupAttributes(request);
        }
        else {
            this.sessionAttributesHandler.storeAttributes(request, defaultModel);
        }
        if (!container.isRequestHandled() && container.getModel() == defaultModel) {
            updateBindingResult(request, defaultModel);
        }
    }

回到RequestMappingHandlerAdapter中的hanlderInternal()方法中钥飞,如果response的響應(yīng)條沒(méi)有設(shè)置Cache-control屬性的話,如果handlerMethod對(duì)應(yīng)的SessionAttributesHandler中維護(hù)了被@SessionAtrribute注解的model衫嵌,那么設(shè)置Cache-control為no store模式读宙。否則設(shè)置Cache-control為-1。

    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        checkRequest(request);

         ...
            // No synchronization on session demanded at all...
            mav = invokeHandlerMethod(request, response, handlerMethod);

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
             // 調(diào)用的是WebContentGeneratorl類中的方法
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
        }

        return mav;
    }

回到DispatcherServlet中的doDispatch()方法渐扮,我們通過(guò)適配器調(diào)用HandlerExecutionChain中的handler返回ModelAndView论悴,如果ModelAndView中沒(méi)有視圖引用,那么申請(qǐng)?jiān)O(shè)置默認(rèn)的視圖名稱墓律。然后調(diào)用HandlerExecutionChain中所有的攔截器中的postHandle()方法膀估,對(duì)handlerMethod返回的結(jié)果進(jìn)行加強(qiáng)處理。

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
                ...

                // 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);
                }
            }
        }
    }

接著調(diào)用processDispatchResult()方法耻讽,開始對(duì)處理程序調(diào)用返回的結(jié)構(gòu)進(jìn)行處理察纯。要么是ModelAndView,要么解析成ModelAndView的異常针肥。如果異常不為空且是ModelAndViewDefiningException類型的異常饼记,那么把視圖解析成ModelAndViewDefiningException特定的視圖。如果異常不為空且不是ModelAndViewDefiningException類型的異常慰枕,那么調(diào)用 processHandlerException()讓HandlerExceptionResovlers中的異常處理器來(lái)處理具则。

    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

    /**
     * Handle the result of handler selection and handler invocation, which is
     * either a ModelAndView or an Exception to be resolved to a ModelAndView.
     */
    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);
                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;
        }

        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

processHandlerException()內(nèi)部實(shí)現(xiàn)是遍歷DispatcherServlet中的handlerExceptionResolvers(程序異常解析器集合),如果當(dāng)前有異常解析器能夠處理這個(gè)異常且處理完畢后返回的ModelAndView不為空具帮,那么跳出循環(huán)博肋。然后繼續(xù)判斷低斋,如果ModelAndView中的model屬性并且view屬性都為空的話,把異常信息放到request中(EXCEPTION_ATTRIBUTE的值是 DispatcherServlet.class.getName() + ".EXCEPTION")匪凡,直接返回null膊畴。


    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;
    }

同時(shí)我們也可以看看HandlerExceptionResolverComposite中的resolveException()方法。通過(guò)遍歷已配置的異常解析器列表來(lái)解決處理異常病游,如果處理返回的ModelAndView實(shí)例不為空唇跨,那么直接返回ModelAndView實(shí)例。

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
            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;
    }

回到DispatcherServlet中的processDispatchResult()衬衬,如果ModelAndView實(shí)例不為空且modelAndView中的model和view屬性不為空买猖,那么進(jìn)行render()操作。render()操作之后佣耐,判斷errorView是否為true政勃,如果為true唧龄,代表已經(jīng)有錯(cuò)誤視圖去響應(yīng)錯(cuò)誤兼砖,那么就可以清理request中一些關(guān)于錯(cuò)誤的屬性(status_code、exception_type既棺、message讽挟、exception、request_uri丸冕、servlet_name)耽梅。

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

        boolean errorView = false;
            ....
        // 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;
        }

        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }
    //WebUtils

    /**
     * Standard Servlet 2.3+ spec request attributes for error pages.
     * <p>To be exposed to JSPs that are marked as error pages, when forwarding
     * to them directly rather than through the servlet container's error page
     * resolution mechanism.
     */
    public static final String ERROR_STATUS_CODE_ATTRIBUTE = "javax.servlet.error.status_code";
    public static final String ERROR_EXCEPTION_TYPE_ATTRIBUTE = "javax.servlet.error.exception_type";
    public static final String ERROR_MESSAGE_ATTRIBUTE = "javax.servlet.error.message";
    public static final String ERROR_EXCEPTION_ATTRIBUTE = "javax.servlet.error.exception";
    public static final String ERROR_REQUEST_URI_ATTRIBUTE = "javax.servlet.error.request_uri";
    public static final String ERROR_SERVLET_NAME_ATTRIBUTE = "javax.servlet.error.servlet_name";

    /**
     * Clear the Servlet spec's error attributes as {@link javax.servlet.http.HttpServletRequest}
     * attributes under the keys defined in the Servlet 2.3 specification:
     * {@code javax.servlet.error.status_code},
     * {@code javax.servlet.error.exception_type},
     * {@code javax.servlet.error.message},
     * {@code javax.servlet.error.exception},
     * {@code javax.servlet.error.request_uri},
     * {@code javax.servlet.error.servlet_name}.
     * @param request current servlet request
     */
    public static void clearErrorRequestAttributes(HttpServletRequest request) {
        request.removeAttribute(ERROR_STATUS_CODE_ATTRIBUTE);
        request.removeAttribute(ERROR_EXCEPTION_TYPE_ATTRIBUTE);
        request.removeAttribute(ERROR_MESSAGE_ATTRIBUTE);
        request.removeAttribute(ERROR_EXCEPTION_ATTRIBUTE);
        request.removeAttribute(ERROR_REQUEST_URI_ATTRIBUTE);
        request.removeAttribute(ERROR_SERVLET_NAME_ATTRIBUTE);
    }

看看render()里面是干嘛的,它實(shí)際上是為了呈現(xiàn)ModelAndView胖烛,這是處理請(qǐng)求的最后一步眼姐,里面涉及到將邏輯視圖轉(zhuǎn)換成真正的物理視圖。

    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;
        if (mv.isReference()) {
            // We need to resolve the view name.
            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;
        }
    }

resolveViewName()其實(shí)將視圖名稱轉(zhuǎn)換成真正的視圖對(duì)象佩番。通過(guò)遍歷當(dāng)前所有的viewResolvers众旗,如果視圖解析器解析后的view對(duì)象不為空的話,那么直接返回當(dāng)前view對(duì)象趟畏。

    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;
    }

進(jìn)入到ContentNegotiatingViewResolver贡歧。ContentNegotiatingViewResolver根據(jù)請(qǐng)求頭中的Accept屬性或者是請(qǐng)求文件名來(lái)解析視圖。它本身不提供解析視圖赋秀,而是通過(guò)viewResolvers集合中的視圖解析器來(lái)解析視圖利朵。resolveViewName()這個(gè)方法,通過(guò)請(qǐng)求頭中的Accept屬性獲取requestMediaTypes猎莲,由此獲取與之兼容的view對(duì)象集合绍弟。再通過(guò)兼容的view對(duì)象集合獲得最佳匹配的view對(duì)象(AbstractMessageConverterMethodProcessor也用了類似的方法,獲得最佳的selectedMediaType)著洼。

    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
        List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
        if (requestedMediaTypes != null) {
            List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
            View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
            if (bestView != null) {
                return bestView;
            }
        }
        if (this.useNotAcceptableStatusCode) {
            if (logger.isDebugEnabled()) {
                logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
            }
            return NOT_ACCEPTABLE_VIEW;
        }
        else {
            logger.debug("No acceptable view found; returning null");
            return null;
        }
    }

獲取到合適的view對(duì)象樟遣,那么調(diào)用其本身的render()方法姥份,將model對(duì)象填充到到view對(duì)象中,完成渲染操作年碘。

            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            view.render(mv.getModelInternal(), request, response);

尾言

對(duì)于一個(gè)框架澈歉,我們不僅要做到熟練使用,還用知其然知其所以然屿衅。以后我再寫關(guān)于框架源碼分析的文章埃难,會(huì)盡量切割,縮小篇幅涤久。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涡尘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子响迂,更是在濱河造成了極大的恐慌考抄,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔗彤,死亡現(xiàn)場(chǎng)離奇詭異川梅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)然遏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門贫途,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人待侵,你說(shuō)我怎么就攤上這事丢早。” “怎么了秧倾?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵怨酝,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我那先,道長(zhǎng)农猬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任胃榕,我火速辦了婚禮盛险,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勋又。我一直安慰自己苦掘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布楔壤。 她就那樣靜靜地躺著鹤啡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蹲嚣。 梳的紋絲不亂的頭發(fā)上递瑰,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天祟牲,我揣著相機(jī)與錄音,去河邊找鬼抖部。 笑死说贝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的慎颗。 我是一名探鬼主播乡恕,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼俯萎!你這毒婦竟也來(lái)了傲宜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤夫啊,失蹤者是張志新(化名)和其女友劉穎函卒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撇眯,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡报嵌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叛本。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沪蓬。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖来候,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逸雹,我是刑警寧澤营搅,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站梆砸,受9級(jí)特大地震影響转质,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜帖世,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一休蟹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧日矫,春花似錦赂弓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至窃诉,卻和暖如春杨耙,著一層夾襖步出監(jiān)牢的瞬間赤套,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工珊膜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留容握,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓车柠,卻偏偏與公主長(zhǎng)得像唯沮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子堪遂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容