死磕Spring原始碼-MVC處理HTTP分發請求

dunne21發表於2021-09-09

MVC處理HTTP分發請求

上面已經完成了HandlerMapping的載入,每一個HandlerMapping持有一系列從URL請求到Controller的對映,這種對映關係通常用一個Map(LinkedHashMap,命名為handlerMap)來持有。
private final Map urlMap = new LinkedHashMap();

某個URL對映到哪個Controller,這部分的配置是在容器對Bean進行依賴注入時發生的,透過Bean的postProcessor來完成,(registerHandler方法)。這樣就為DispatcherServlet的分發奠定了資料基礎。

SimpleUrlHandlerMapping註冊handler的程式碼邏輯

SimpleUrlHandlerMapping.initApplicationContext
@Override
public void initApplicationContext() throws BeansException {
    super.initApplicationContext();
    registerHandlers(this.urlMap);
}

protected void registerHandlers(Map urlMap) throws BeansException {
    if (urlMap.isEmpty()) {
        logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
    }
    else {
        for (Map.Entry entry : urlMap.entrySet()) {
            String url = entry.getKey();
            Object handler = entry.getValue();
            // 確保url以"/"開頭
            if (!url.startsWith("/")) {
                url = "/" + url;
            }
            // 去掉空格
            if (handler instanceof String) {
                handler = ((String) handler).trim();
            }
            registerHandler(url, handler);
        }
    }
}

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
    Assert.notNull(urlPath, "URL path must not be null");
    Assert.notNull(handler, "Handler object must not be null");
    Object resolvedHandler = handler;

    if (!this.lazyInitHandlers && handler instanceof String) {
        String handlerName = (String) handler;
        if (getApplicationContext().isSingleton(handlerName)) {
            resolvedHandler = getApplicationContext().getBean(handlerName);
        }
    }

    Object mappedHandler = this.handlerMap.get(urlPath);
    if (mappedHandler != null) {
        if (mappedHandler != resolvedHandler) {
            throw new IllegalStateException(
                    "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                    "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
        }
    }
    else {
        //如果url是“/”,則這個url對映的Controller就是rootHandler
        if (urlPath.equals("/")) {
            if (logger.isInfoEnabled()) {
                logger.info("Root mapping to " + getHandlerDescription(handler));
            }
            setRootHandler(resolvedHandler);
        }//如果url是“/*”,則這個url對映的Controller就是DefaultHandler
        else if (urlPath.equals("/*")) {
            if (logger.isInfoEnabled()) {
                logger.info("Default mapping to " + getHandlerDescription(handler));
            }
            setDefaultHandler(resolvedHandler);
        }
        else {//如果url是正常的url,設定handlerMap的key和value分別對應url和對應的Controller
            this.handlerMap.put(urlPath, resolvedHandler);
            if (logger.isInfoEnabled()) {
                logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
            }
        }
    }
}

準備好handlerMap中的資料後,我們來看看handlerMapping如何完成請求的對映處理。

在HandlerMapping中定義了getHandler方法,這個方法會根據我們剛剛初始化得到的handlerMap來獲取與HTTP請求對應的HandlerExecutionChain,它封裝了具體的Controller物件。HandlerExecutionChain有兩個比較重要:攔截器(Interceptor)鏈和handler物件,handler物件就是HTTP對應的Controller,透過攔截器鏈裡的攔截器來對handler物件提供功能的增強。

AbstractHandlerMapping.getHandler
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //獲取request對應的handler
    Object handler = getHandlerInternal(request);
    //獲取預設的handler
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // 根據名稱取出對應的Handler Bean
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }
    //把這個handler封裝到HandlerExecutionChain中並加上攔截器
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    if (CorsUtils.isCorsRequest(request)) {
        CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }
    return executionChain;
}

**獲取request對應的handler的程式碼邏輯**

@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    //從request獲取url路徑
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    //從HandlerMap中獲取指定url路徑的handler
    Object handler = lookupHandler(lookupPath, request);
    if (handler == null) {
        // 如果沒有對應url路徑的handler則返回相應的handler(RootHandler或DefaultHandler)
        Object rawHandler = null;
        if ("/".equals(lookupPath)) {
            rawHandler = getRootHandler();
        }
        if (rawHandler == null) {
            rawHandler = getDefaultHandler();
        }
        if (rawHandler != null) {
            if (rawHandler instanceof String) {
                String handlerName = (String) rawHandler;
                rawHandler = getApplicationContext().getBean(handlerName);
            }
            validateHandler(rawHandler, request);
            handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
        }
    }
    if (handler != null && logger.isDebugEnabled()) {
        logger.debug("Mapping [" + lookupPath + "] to " + handler);
    }
    else if (handler == null && logger.isTraceEnabled()) {
        logger.trace("No handler mapping found for [" + lookupPath + "]");
    }
    return handler;
}

**得到handler物件後,把這個handler封裝到HandlerExecutionChain中並加上攔截器**

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

現在我們完成了對HandlerExecutionChain的封裝工作,為handler對http請求響應做好了準備。

DispatcherServlet是HttpServlet的子類,和其他的HttpServlet一樣,透過doService方法來響應HTTP請求(再呼叫doDispatch)。DispatcherServlet透過getHandler得到一個HandlerExecutionChain後,透過HandlerAdapter來驗證這個handler的合法性(handler instanceof Controller,如果是Controller的物件則返回true,反之返回false),合法後執行handler方法獲取結果資料,這些資料都封裝在ModelAndView中返回給前端進行檢視呈現,對檢視呈現的處理是透過呼叫入口:render方法來實現的。

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

圖片描述

參考文獻:

《Spring技術內幕 深入解析Spring架構與設計原理》

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1600/viewspace-2799202/,如需轉載,請註明出處,否則將追究法律責任。

相關文章