1. 程式人生 > >SpringMVC執行流程及原始碼解析

SpringMVC執行流程及原始碼解析

在SpringMVC中主要是圍繞著DispatcherServlet來設計,可以把它當做指揮中心。這裡先說明一下SpringMVC文件給出的執行流程,然後是我們稍微具體的執行流程,最後是流程大致的原始碼跟蹤。關於很很很詳細的原始碼解析,這裡暫先不做。

官方文件中的流程

首先看下SpringMVC文件上給的流程圖:

這張圖片給了我們大概的執行流程:

  • 使用者請求首先發送到前端控制器DispatcherServlet,DispatcherServlet根據請求的資訊來決定使用哪個頁面控制器Controller(也就是我們通常編寫的Controller)來處理該請求。找到控制器之後,DispatcherServlet將請求委託給控制器去處理。
  • 接下來頁面控制器開始處理使用者請求,頁面控制器會根據請求資訊進行處理,呼叫業務層等等,處理完成之後,會把結果封裝成一個ModelAndView返回給DispatcherServlet。
  • 前端控制器DispatcherServlet接到頁面控制器的返回結果後,根據返回的檢視名選擇相應的試圖模板,並根據返回的資料進行渲染。
  • 最後前端控制器DispatcherServlet將結果返回給使用者。

更具體的流程

上面只是總體流程,接下來我們稍微深入一點,看下更具體的流程,這裡沒有圖,只有步驟解析:

  • 使用者請求傳送到前端控制器DispatcherServlet。
  • 前端控制器DispatcherServlet接收到請求後,DispatcherServlet會使用HandlerMapping來處理,HandlerMapping會查詢到具體進行處理請求的Handler物件。
  • HandlerMapping找到對應的Handler之後,並不是返回一個Handler原始物件,而是一個Handler執行鏈,在這個執行鏈中包括了攔截器和處理請求的Handler。HandlerMapping返回一個執行鏈給DispatcherServlet。
  • DispatcherServlet接收到執行鏈之後,會呼叫Handler介面卡去執行Handler。
  • Handler介面卡執行完成Handler(也就是我們寫的Controller)之後會得到一個ModelAndView,並返回給DispatcherServlet。
  • DispatcherServlet接收到Handler介面卡返回的ModelAndView之後,會根據其中的檢視名呼叫檢視解析器。
  • 檢視解析器根據邏輯檢視名解析成一個真正的View檢視,並返回給DispatcherServlet。
  • DispatcherServlet接收到檢視之後,會根據上面的ModelAndView中的model來進行檢視渲染完成之後,DispatcherServlet就可以將結果返回給使用者了。

原始碼

DispatcherServlet是一個Servlet,我們知道在Servlet在處理一個請求的時候會交給service方法進行處理,這裡也不例外,DispatcherServlet繼承了FrameworkServlet,首先進入FrameworkServlet的service方法:

protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
	//請求方法
    String method = request.getMethod();
    //PATCH方法單獨處理
    if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
        processRequest(request, response);
    }
    else {//其他的請求型別的方法經由父類,也就是HttpServlet處理
        super.service(request, response);
    }
}

HttpServlet中會根據請求型別的不同分別呼叫doGet或者doPost等方法,FrameworkServlet中已經重寫了這些方法,在這些方法中會呼叫processRequest進行處理,在processRequest中會呼叫doService方法,這個doService方法就是在DispatcherServlet中實現的。下面就看下DispatcherServlet中的doService方法的實現。

請求到達DispatcherServlet doService方法:

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

    //給request中的屬性做一份快照
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        logger.debug("Taking snapshot of request attributes before include");
        attributesSnapshot = new HashMap<String, Object>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    //如果我們沒有配置類似本地化或者主題的處理器之類的
    //SpringMVC會使用預設的值
    //預設配置檔案是DispatcherServlet.properties
    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()) {
            return;
        }
        // Restore the original attribute snapshot, in case of an include.
        if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
        }
    }
}

DispatcherServlet開始真正的處理, doDispatch方法 :

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
	//SpringMVC中非同步請求的相關知識,暫先不解釋
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

        try {
        	//先檢查是不是Multipart型別的,比如上傳等
            //如果是Multipart型別的,則轉換為MultipartHttpServletRequest型別
            processedRequest = checkMultipart(request);
            multipartRequestParsed = processedRequest != request;

            //獲取當前請求的Handler
            mappedHandler = getHandler(processedRequest, false);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            //獲取當前請求的Handler介面卡
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 對於header中last-modified的處理
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
			//攔截器的preHandle方法進行處理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            try {
                //真正呼叫Handler的地方
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
            }
			//處理成預設檢視名,就是新增字首和字尾等
            applyDefaultViewName(request, mv);
            //攔截器postHandle方法進行處理
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        //處理最後的結果,渲染之類的都在這裡
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Error err) {
        triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            return;
        }
        // Clean up any resources used by a multipart request.
        if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
        }
    }
}

可以看到大概的步驟還是按照我們上面分析的走的。

查詢請求對應的Handler物件

對應著這句程式碼mappedHandler = getHandler(processedRequest, false);,看下具體的getHandler方法:

protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
    return getHandler(request);
}

繼續往下看getHandler:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//遍歷所有的handlerMappings進行處理
    //handlerMappings是在啟動的時候預先註冊好的
    for (HandlerMapping hm : this.handlerMappings) {
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

繼續往下看getHandler,在AbstractHandlerMapping類中:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//根據request獲取handler
    Object handler = getHandlerInternal(request);
    if (handler == null) {
    	//如果沒有找到就使用預設的handler
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    //如果Handler是String,表明是一個bean名稱
    //需要超照對應bean
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }
    //封裝Handler執行鏈
    return getHandlerExecutionChain(handler, request);
}

根據requrst獲取handler

首先看下根據requrst獲取handler步驟getHandlerInternal方法,在AbstractHandlerMethodMapping中:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    //獲取request中的url,用來匹配handler
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    //根據路徑尋找Handler
    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    //根據handlerMethod中的bean來例項化Handler並新增進HandlerMethod
    return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
}

看下根據路徑尋找handler的方法lookupHandlerMethod:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<Match>();
	//直接匹配
    List<T> directPathMatches = this.urlMap.get(lookupPath);
    //如果有匹配的,就新增進匹配列表中
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
	//還沒有匹配的,就遍歷所有的處理方法查詢
    if (matches.isEmpty()) {
        // No choice but to go through all mappings
        addMatchingMappings(this.handlerMethods.keySet(), matches, request);
    }
	//找到了匹配的
    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        Collections.sort(matches, comparator);
		//排序之後,獲取第一個
        Match bestMatch = matches.get(0);
        //如果有多個匹配的,會找到第二個最合適的進行比較一下
        if (matches.size() > 1) {
            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 + "}");
            }
        }
		//設定request引數
        handleMatch(bestMatch.mapping, lookupPath, request);
        //返回匹配的url的處理的方法
        return bestMatch.handlerMethod;
    }
    else {//最後還沒有找到,返回null
        return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
    }
}

獲取預設Handler

如果上面沒有獲取到Handler,就會獲取預設的Handler。如果還獲取不到就返回null。

處理String型別的Handler

如果上面處理完的Handler是String型別的,就會根據這個handlerName獲取bean。

封裝Handler執行鏈

上面獲取完Handler,就開始封裝執行鏈了,就是將我們配置的攔截器加入到執行鏈中去,getHandlerExecutionChain:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	//如果當前Handler不是執行鏈型別,就使用一個新的執行鏈例項封裝起來
    HandlerExecutionChain chain =
        (handler instanceof HandlerExecutionChain) ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
	//先獲取適配型別的攔截器新增進去攔截器鏈
    chain.addInterceptors(getAdaptedInterceptors());
	//當前的url
    String lookupPath = urlPathHelper.getLookupPathForRequest(request);
    //遍歷攔截器,找到跟當前url對應的,新增進執行鏈中去
    for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
        if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
        }
    }

    return chain;
}

獲取對應請求的Handler介面卡

getHandlerAdapter:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	//遍歷所有的HandlerAdapter,找到和當前Handler匹配的就返回
    //我們這裡會匹配到RequestMappingHandlerAdapter
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (ha.supports(handler)) {
            return ha;
        }
    }
}

快取的處理

也就是對last-modified的處理

執行攔截器的preHandle方法

就是遍歷所有的我們定義的interceptor,執行preHandle方法

使用Handler介面卡執行當前的Handler

ha.handle執行當前Handler,我們這裡使用的是RequestMappingHandlerAdapter,首先會進入AbstractHandlerMethodAdapter的handle方法:

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    return handleInternal(request, response, (HandlerMethod) handler);
}

handleInternal方法,在RequestMappingHandlerAdapter中:

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

    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
        // Always prevent caching in case of session attribute management.
        checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    }
    else {
        // Uses configured default cacheSeconds setting.
        checkAndPrepare(request, response, true);
    }

    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                return invokeHandleMethod(request, response, handlerMethod);
            }
        }
    }
	//執行方法,封裝ModelAndView
    return invokeHandleMethod(request, response, handlerMethod);
}

組裝預設檢視名稱

字首和字尾名都加上

執行攔截器的postHandle方法

遍歷intercepter的postHandle方法。

處理最後的結果,渲染之類的

processDispatchResult方法:

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) {
            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 (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

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

重點看下render方法,進行渲染:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    //設定本地化
    Locale locale = this.localeResolver.resolveLocale(request);
    response.setLocale(locale);

    View view;
    if (mv.isReference()) {
        //解析檢視名,得到檢視
        view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
    }
    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() + "'");
        }
    }

    //委託給檢視進行渲染
    view.render(mv.getModelInternal(), request, response);
}

view.render就是進行檢視的渲染,然後跳轉頁面等處理。

到這裡大概的流程就走完了。其中涉及到的東西還有很多,暫先不做詳細處理。