SpringMvc請求處理流程與源碼探祕

流程梳理

dispatcherServlet作爲前端控制器的主要作用就是接受請求與處理響應。

不過它不是傳統意義上的servlet,它在接受到請求後採用轉發的方式,將具體工作交給專業人士去做。

參與角色主要有:

前端控制器(DispatcherServlet)

處理映射器(HandlerMapping)

處理適配器(HandlerAdapter)

處理器((Handler)Controller)

視圖解析器(ViewReslover)

視圖(View)


找了一張圖,把請求過程與步驟清晰的呈現了出來

 

第一步:前端控制器dispatcher接受請求

    Client---url--->Dispatcher

第二步:前端控制器去發起handler映射查找請求

    Dispatcher---HttpServletRequest---> HandlerMapping

第三步:處理器映射器查找hanlder並返回HandlerExetuionChain

     Dispatcher <---HandlerExeutionChain---HandlerMapping

第四步:前端控制器發起請求處理器適配器請求執行

  Dispatcher---Handler---> HandlerAdapter

第五步:處理器適配器去調用handler執行

HandlerAdapter---HttpServletRequest> Handler(Controller)

第六步:處理器處理後返回ModelAndView給HandlerAdapter

HandlerAdapter <---ModelAndView---Handler(Controller)

第七步:處理器適配器將ModelAndView返回給前端控制器

Dispatcher <---ModelAndView---HandlerAdapter

第八步:前端控制器請求視圖解析器解析ModelAndView

Dispatcher---ModelAndView---> ViewReslover

第九步:視圖解析器解析視圖後返回視圖View給前端控制器

Dispatcher <---View---ViewReslover

第十步:前端控制器請求視圖要求渲染視圖

Dispatcher--->View--->render

第十一步:前端控制器返回響應

Response <---Dispatcher

源碼探祕

  第一步接受請求:

我們可以來看看DispatcherServlet的繼承結構


其實DispatcherServlet能處理請求是因爲HttpServlet類的service方法,而HttpServlet又來自Servlet接口定義的規範。


可以看到抽象類HttpServlet實現了接口Servlet的service方法,根據請求類型不同執行了不同的方法(doGet,doPost)


當請進來後,由HttpServlet的子類FrameworkServlet重寫的service方法執行請求,可以看到437行子類調用了父類的service方法,然後在父類執行doGet之類的方法時,由於子類FrameworkServlet重寫了父類方法,交由子類執行,所以進到了我的doGet斷點裏面,它調用了處理請求方法。

接下來我們看看ProcessRequest方法的源碼

 protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = this.buildLocaleContext(request);
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
            this.doService(request, response);
        } catch (IOException | ServletException var16) {
            failureCause = var16;
            throw var16;
        } catch (Throwable var17) {
            failureCause = var17;
            throw new NestedServletException("Request processing failed", var17);
        } finally {
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }

 

前面一系列初始化工作我們先不管,看看重要的部分,try裏面的doService方法


跟蹤進去看了一下,由於它是抽象方法,所以會由子類實現和執行,也就是我們的DispatchServlet類了


老規矩,先貼上源碼,它是DispatchServlet的doService方法--------------------------------------------------------------------------------------------------------------

 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();

            label95:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label95;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }

        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        if (this.flashMapManager != null) {
            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 {
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }

 

所以第一步也就完成了,第一步的任務就是走進這裏來。

第二步:前端控制器去發起handler映射查找請求

Dispatcher---HttpServletRequest---> HandlerMapping

上面的源碼中主要工作就是給request實例設置一系列參數,要注意的就是doDispatch方法,這裏面就是mvc的核心了,前面第一張交互圖裏面的流程都是在這裏實現的。


可以看到,通過HttpRequestServlet作爲參數請求handlerMapping

第三步:處理器映射器查找hanlder並返回HandlerExetuionChain

     Dispatcher <---HandlerExeutionChain---HandlerMapping

可以看到上圖中返回了mappedHandler變量,就是HandlerExtuceChain類型


可以看到,已經找到並返回了我們的HomeController處理器(Hanlder)

第四步:前端控制器發起請求處理器適配器請求執行

  Dispatcher---Handler---> HandlerAdapter


從508行可以看到,適配器傳入handler對象和reaquest等信息,執行handler()方法

第五步:處理器適配器去調用handler執行

HandlerAdapter---HttpServletRequest> Handler(Controller)


從這裏可以看到,調用的handlerInternal是個抽象方法,會調用子類的實現方法,子類由RequestMappingHandlerAdapter實現,這個類也是我們經常在xml裏面配置的類


通過invokeHandlerMethod方法執行進到controller裏面


方法執行後返回我們的index


第六步:處理器處理後返回ModelAndView給HandlerAdapter

HandlerAdapter <---ModelAndView---Handler(Controller)

通過調用invokeHandlerMethod方法返回ModelAndView


第七步:處理器適配器將ModelAndView返回給前端控制器

Dispatcher <---ModelAndView---HandlerAdapter


第八步:前端控制器請求視圖解析器解析ModelAndView

Dispatcher---ModelAndView---> ViewReslover


第九步:視圖解析器解析視圖後返回視圖View給前端控制器

Dispatcher <---View---ViewReslover


可以看到,返回的視圖,url指向index.jsp頁面

第十步:前端控制器請求視圖要求渲染視圖

Dispatcher--->View--->render

如果View對象不爲空,將會調用render方法渲染


如果返回的是json對象,屬於接口的,是不會走這裏的


此時會找對應的視圖解析器去渲染


裏面其實也沒幹啥,就做了個跳轉,到jsp頁面去綁定數據


第十一步:前端控制器返回響應

Response <---Dispatcher


到這裏也就基本上完了。


處理請求完成後做了個重置工作,然後發佈一個事件,你可以選擇監聽這個事件,做相應處理。

再看看response裏面


這個就是我們頁面上的內容了。

全劇終

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