天天看點

spring MVC mybatis dispacherServlet(源碼解讀)

以下源碼版本(4.2.0.RELEASE)

dispacherServlet是servlet的實作類,是spring MVC的前端轉發器,是spring MVC的核心。

那麼它做了哪些事呢?

它主要做了兩件事:

NO1:

看如下源碼:

/**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
protected void initStrategies(ApplicationContext context) {  
          //初始化分段上傳解析器(成對檔案上傳的解析和封裝工作),沒有預設的實作    
          initMultipartResolver(context);  
          //初始化地域解析器,預設實作是AcceptHeaderLocaleResolver  
          initLocaleResolver(context);  
           //初始化主題解析器,預設實作是FixedThemeResolver  
          initThemeResolver(context);  
           //初始化處理器映射,這是個集合, 預設實作是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping   
          initHandlerMappings(context);  
           //初始化處理器擴充卡,這是個集合,預設實作是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter    
          initHandlerAdapters(context);  
            //初始化處理器異常解析器,這是個集合,預設實作是AnnotationMethodHandlerExceptionResolver,ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver   
          initHandlerExceptionResolvers(context);  
          //初始化請求到視圖名解析器,預設實作是DefaultRequestToViewNameTranslator    
          initRequestToViewNameTranslator(context);  
          //初始化視圖解析器,這是個集合,預設實作是InternalResourceViewResolver    
          initViewResolvers(context);  
     }        

NO2:

每次請求都會調用它的doService方法,在doService方法中調用它的doDispatch方法。

doService方法可看代碼中的兩處注釋:

spring MVC mybatis dispacherServlet(源碼解讀)

 重點在于調用它的doDispatch方法:

doDispatch(request, response);      

 首先我們來看方法doDispatch的注釋:

spring MVC mybatis dispacherServlet(源碼解讀)

可以簡單了解成:所有的請求都将在這個方法裡映射到指定的action的指定的方法(前提:請求路徑正确)。

我們來一步一步解析這個方法:

以下,為

doDispatch方法的源碼:      
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// processedRequest是經過checkMultipart方法處理過的request請求
        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;
            }
            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
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }      

 第一行建立的變量processedRequest 會在

 後面某一行(在doDispatch方法中,如下:)

processedRequest = checkMultipart(request);      

 中被方法處理加工,checkMultipart方法也是dispacherServlet類中的一個方法,源碼如下:

spring MVC mybatis dispacherServlet(源碼解讀)

 那麼這個方法的作用是什麼呢?

關鍵是下面這一句:

this.multipartResolver != null && this.multipartResolver.isMultipart(request)      

 這一句代碼做了如下兩個判斷:

1.dispatcherServlet中初始化後的multipartResolver是否為空(配置檔案中是否配置,初始化dispatcherServlet時沒有預設執行個體);

2.multipartResolver的方法isMultipart的作用是判斷本次請求是否為檔案上傳。

如下為isMultipart的源碼:

spring MVC mybatis dispacherServlet(源碼解讀)

通過注釋,可簡單了解為:該方法通常以檢查content-type是否為multipart/form-data的方式判斷本次請求是否為檔案上傳,但實際功能需要看具體解析器對該方法的實作細節。

checkMultipart方法大緻如此,再具體一些的内容在這裡我就不贅述了,有興趣的朋友可以自己看源碼。

 (多嘴一句:若想使用multipartResolver不要忘了增加jar包支援:commons.fileupload,common.io).

---------------------------------------

方法doDispatch的第二行建立了變量mappedHandler ,

類型為HandlerExecutionChain ,通過檢視該類的源碼,可清楚其作用,如下:

spring MVC mybatis dispacherServlet(源碼解讀)

可了解其為一個處理鍊,包含了所有的handler和攔截器。

 以下三行代碼為doDispatch的核心:

// Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);      
// Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());      
// Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());      

 通過注釋,可了解前一句的作用:找到指定的handler,

後一句:找到對應的擴充卡adapter,

第三句:在這個函數裡面會真正執行request請求相對于handler的方法(這隻是大概的代碼流程,真正的調用方法之前還有很多先前處理。)

以下是我debug斷點的顯示資料:

spring MVC mybatis dispacherServlet(源碼解讀)

該方法時DispatcherServlet類中的方法getHandler,可以看到這裡會對所有handlerMapper進行周遊,後執行HandlerMappering的getHandler方法,

如下:為HandlerMappering的getHandler方法:

spring MVC mybatis dispacherServlet(源碼解讀)

這個方法會傳回一個handler和所有的攔截器,組裝成了一個HandlerExecutionChain類。也就是在方法doDispatch第二行代碼裡定義的mappedHandler,

該方法的具體實作是在org.springframework.web.servlet.handler.AbstractHandlerMapping中,如下:

/**
     * 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.corsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }      

這裡的getHandlerInternal是個抽象方法,有具體的HandlerMapping來實作,擷取到的handler如果為空,則擷取預設配置的handler,若為string類型,則表示要去spring配置容器中去找這個bean,

該方法剩下的部分正如HandlerMappering的getHandler方法注釋所描述的:傳回一個handler和所有的攔截器(有興趣可深入了解)。

 --------------------------

 再來看doDispath方法裡的

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

它首先調用了dispatcherServlet裡的 getHandlerAdapter方法;看其源碼如下:

spring MVC mybatis dispacherServlet(源碼解讀)

看其注釋:該方法會通過已确定映射的handler類來尋找滿足條件的adapter,(注意,若沒有找到,會直接報servletException異常)。

這個方法的核心是 方法supports,這是個抽象方法,不同的擴充卡會有不同的内部細節,如下分别為

SimpleControllerHandlerAdapter和SimpleServletHandlerAdapter的實作:      
spring MVC mybatis dispacherServlet(源碼解讀)
spring MVC mybatis dispacherServlet(源碼解讀)

通過supports方法,dispatcherervlet類可以确定本次請求request映射的adapter.

------------------------

最後看

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

這裡的mv是一個ModelAndView類,ha是前面獲得的擴充卡adapter,

方法handler()是個抽象方法,具體實作細節依賴于擴充卡adapter的實作類,

但我門可看其注釋,了解該方法的作用,

源碼如下:

spring MVC mybatis dispacherServlet(源碼解讀)

其注釋的意思是是:使用提供的handler(前面确定的handler)來處理這個request請求。

以下,已實作類SimpleControllerHandlerAdapter為例,

spring MVC mybatis dispacherServlet(源碼解讀)

這裡會執行handlerRequest(該方法的實作細節依賴于不同的實作類,最後傳回一個ModelAndAndView。

以上,為我本次研讀dispatcherServlet類的所得。

可總結為,dispatcherServlet根據預設或配置的handlermapping和adapter,選擇不同的方式對請求request請求進行處理,最後傳回一個視圖ModeAndView。

(那麼傳回值為json是如何實作的?····問題越來越多了)

----------------------------------補充----------------------------

可以看出,關于

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

部分,闡述的比較模糊,或者說,基本沒什麼描述,雖然這部分是核心,

我試圖過闡述其實作細節,但這部分的實作細節根據不同的handlerMapping和不同的adpater而不同,

是以,我準備在其他章節對其進行分别闡述。

轉載于:https://www.cnblogs.com/zqsky/p/6184436.html