天天看點

springmvc工作流程_SpringMVC執行流程(源碼分析)

本文已托管到GitHub,大家可以去GitHub檢視下載下傳!并搜尋關注微信公衆号 【碼出Offer】 領取各種學習資料!

GitHub​github.com

SpringMVC執行流程

SpringMVC概述

Spring MVC屬于SpringFrameWork的後續産品,已經融合在Spring Web Flow裡面。Spring 架構提供了建構 web 應用程式的全功能 MVC 子產品。使用 Spring 可插入的 MVC 架構,進而在使用Spring進行WEB開發時,可以選擇使用Spring的Spring MVC架構或內建其他MVC開發架構。

SpringMVC執行流程概括

SpringMVC架構固然強大,但是其執行流程更是妙不可言。是以我們這次要用一個簡單的例子去深究一下SpringMVC的底層執行流程!

如下是SpringMVC的執行流程梗概圖,我會在後面的底層流程剖析中重點提到梗概圖中的這幾個零件,以及它們的作用!

springmvc工作流程_SpringMVC執行流程(源碼分析)

SpringMVC的重要元件(可視化元件)

既然,我們要選擇剖析SpringMVC底層執行流程,那肯定是要先分析我們能所看到表面的MVC重要元件。這樣我們分析完可視元件後,就能找到分析SpringMVC底層執行流程的入口,是以分析它的重要元件顯得更是重要!

SpringMVC的重要元件是由

核心的前端控制器(web.xml)

後端控制器(Controller)

spring-mvc.xml配置檔案

組成。

  • 核心的前端控制器: 作為MVC架構,首先要解決的就是如何能收到請求。是以MVC架構大都會設計一款前端控制器(入口或者說起點),選型在Servlet或Filter兩者之一,由前端控制器來最率先的工作,接收請求。在SpringMVC中,也不例外,前端控制器的選型确定為Servlet(DispatcherServlet),此前端控制器在接收請求後,還會負責SpringMVC的核心排程管理,是以既是前端又是核心。
  • 後端控制器: 後端控制器為Controller,等價于之前定義的Servlet。MVC架構中,後端控制器也是必不可少的重要元件之一。因為它接收了使用者請求的大量資料參數對象(或Json)存儲在域中友善頁面(JSP)取值,或是攜帶着這些資料傳回所需要跳轉(重定向或請求轉發)的頁面。這裡值得注意的是,後端控制器本質并不是一個普通的Servlet,也不是BaseServlet,它隻是一個普通的類,裡面卻像曾經的BaseServlet一樣可以擁有很多個方法,這些方法在SpringMVC中成為一個個Handler(換湯不換藥,本質仍然)。是以在MVC模式的執行流程環節中,後端控制器控制着頁面的跳轉和資料的傳遞,在這裡也有着很高的地位。
  • spring-mvc.xml配置檔案: 該配置檔案配置着許多在執行過程中需要加載的元件,比如:注解掃描器、注解掃描驅動、試圖解析器、靜态資源處理器、異常解析器、攔截器、上傳解析器等等,如果我們要使用這些元件,就需要在該配置檔案中注入這些元件的相關配置,注入配置後由SpringMVC工廠在執行過程中加載這些元件,以達成我們使用這些元件的目的。是以這也是它受人青睐的原因。

SpringMVC執行流程剖析

上述得知,我們執行流程剖析的入口既是

核心的前端控制器,即web.xml

,那我們有資格了解該前端控制器中配置了什麼!如下:

springmvc工作流程_SpringMVC執行流程(源碼分析)

由上圖所知,前端控制器中所包含的即是同時啟動SpringMVC工廠和Spring工廠,讓兩個工廠同時運作處理請求,并作出響應。既然要剖析SpringMVC的底層執行流程,那我們要從加載SpringMVC工廠的

DispatcherServlet

說起。首先進入到DispatcherServlet中,檢視源代碼所有方法,如下圖所示:

springmvc工作流程_SpringMVC執行流程(源碼分析)

上圖所示,我進入到了DispatcherServlet中。既然說它是一個Servlet,那肯定是需要尋找它的service方法,因為Service方法是Servlet的核心所在。于是我打開了IDEA的方法清單搜尋service方法,未果。雖然未果,但是我發現兩個重要的線索,一是該Servlet中有一個doSerivce方法,二是DispatcherServlet繼承了FrameworkServlet,我想既然子類沒有service方法,父類肯定有,于是我進入到了FrameworkServlet檢視源代碼,如下圖所示:

springmvc工作流程_SpringMVC執行流程(源碼分析)

我興沖沖在父類(FrameworkServlet)中找到了service方法,但是還是感覺高興的太早了,該service方法中除了

resolve

方法擷取請求方式和

processRequest

方法外,我一無所知。随後竟然發現了紅色箭頭所指向的東西

super.service(request, response);

,這意味着什麼呢?這意味着它繼承了父類擁有的service方法,于是我點選super句點後面的service方法檢視源碼驚人的發現這個類竟然是HttpServlet,顯然我們找service方法的這條路走到盡頭了。在裡面有兩個方法存在一個是

resolve

方法,它是擷取請求方式的。還有一個方法不知道是做什麼的,于是我點選了進去檢視源碼,如下圖所示:

springmvc工作流程_SpringMVC執行流程(源碼分析)

既然我們進去看到了processRequest方法的源碼,就要找重要的方法。何為重要的方法呢,一般被try塊包裹的方法必然是重要方法,于是我找到了

doService(request, response);

方法,并繼續點選去看該doService方法的源碼,如下圖所示:

springmvc工作流程_SpringMVC執行流程(源碼分析)

逐漸失去耐心的我真的被驚訝到了,進入到doService方法後,也沒有跳到其他的類中,而卻還是在該類中跳到了一個空的doService();方法中。唉,探究究竟真的是件不容易的事情呀~我歎了一口氣。冷靜下來一想,父類是空方法沒有實作,那核心邏輯代碼必定是在子類中了呀。這不是多态嘛!于是,我得出了結論,費勁吧難,找入口的邏輯代碼回過頭來還是得看DispatcherServlet中的那個doService方法。此時我知道,這必将是一個漫長的探索之路。于是,我秉着探究原理的心态,再一次點進了被我錯過的那個DispatcherServlet中的doSerivce方法,如下圖:

springmvc工作流程_SpringMVC執行流程(源碼分析)

既然确定了這是探究底層原理的開始,那我們就在doServie()方法中尋找重要的邏輯,于是我再一次的在try塊中找到了一個名為

doDispatch(request, response);

的方法(省略了前面的各種初始化和存儲域資料)。在探究底層原理的道路上,你會發現越來越接近真理,雖然這注定是一個漫長的探索過程,我也情願。于是,點選進入到了doDispatch()方法中的源碼,如下圖所示:

springmvc工作流程_SpringMVC執行流程(源碼分析)

走進了doDispatch()方法的源碼,才知道我沒有看錯你。裡面标有注釋的都是一些重要的執行邏輯方法。接下來我們會一個個的分析,逐漸深入了解SpringMVC的執行流程。既然探索執行流程那就少不了Debug(Debug調試功能,Debug能很清晰的看到執行流程),于是我在

getHandler()

方法的那一行打了一個斷點。下一步跟進執行流程進入到了

getHandler()

方法,如下圖所示:

springmvc工作流程_SpringMVC執行流程(源碼分析)

斷點停留到了這一行,因為

getHandler()

的名字,顧名思義就是擷取Controller層中的Handler。它是怎麼擷取到的呢?我們在斷點的變量顯示框中,看到handlerMappings是一個數組,其中有三個對象。他們可以分别以不同的方式處理不同的Handler,其中我們可以點選這個三個對象,一一把其對象展開檢視重要屬性,如下圖所示:

springmvc工作流程_SpringMVC執行流程(源碼分析)

如上圖得知,RequestMappingHandlerMapping對象識别了我們Controller中的@RequestMapping注解和各個Handler上方的注解路徑。SimpleUrlHandlerMapping對象識别了處理靜态資源驅動所建立的那個預設Servlet,而處理靜态資源的預設Servlet路徑給了

/**

,它識别了這個路徑。HanderMapping映射器中的對象,通過注解識别擷取到了Controller層的各個Handler請求路徑注解後,就執行到了下一行,如下圖:

springmvc工作流程_SpringMVC執行流程(源碼分析)

通過注解可以找到所有的Handler,其中所有的Handler就存儲在

handlerMappings

中,于是它就周遊了此對象。随後根據各自的請求對象擷取對應的Handler并判空傳回擷取到的對應Handler對象。繼續向下執行,你還會發現這麼一個東西,如下圖:

springmvc工作流程_SpringMVC執行流程(源碼分析)

對,你會發現即将傳回的Handler是一個名為HandlerExecutionChain的執行鍊。其中執行鍊内包含了即将傳回的handler對象和一個interceptorList集合,其中集合内有兩個對象,這兩個對象就是攔截器。是以,不管是你自己使用了攔截器還是沒有使用攔截器(内部底層有攔截器),這些攔截器和handler對象會以一個鍊條的形式執行(攔截器在前,handler對象在後)。則執行過程是遵循着先執行攔截器,後傳回并執行handler對象的順序。傳回了HandlerExecutionChain執行鍊,那麼就要開始執行執行鍊了!問題來了,究竟是誰依次執行攔截器和handler對象呢?如下圖:

springmvc工作流程_SpringMVC執行流程(源碼分析)

傳回執行鍊後,繼續執行就執行到了這一行代碼,其注釋解釋為為目前請求對象尋找一個handler擴充卡。如果你學過擴充卡設計模式也許你會更容易了解,沒有學過也沒有關系,随後的解釋你也可以了解的。知道了它要為請求對象尋找擴充卡,那麼我們繼續執行,就得到了如下啊資訊:

springmvc工作流程_SpringMVC執行流程(源碼分析)

執行流程進入到了

getHandlerAdapter

方法,遠遠看到這個方法有一種似曾相識的感覺,對,它和HandlerMapping映射器很像,簡直就是孿生兄弟。該方法要根據目前傳回的handler對象,為其handler對象尋找一個擴充卡,而handlerAdapters集合對象中就存儲着三個擴充卡,想想我們在映射器中擷取執行鍊的時候是不是也三個呢?對的,他們是成對出現的,handler的對象找其對應的擴充卡才可以繼續執行下去。找到與目前handler對象成對的擴充卡之後,就傳回了該擴充卡。擴充卡傳回後中間經過了如下方法:

springmvc工作流程_SpringMVC執行流程(源碼分析)

中間經過了這一段代碼,擷取了請求對象的請求方式并對此進行了一系列的判斷操作。繼續執行到了下面,下面有一個if判斷,判斷執行了

applyPreHandler

方法,此方法就是攔截器的前置方法。執行完攔截器的前置方法後,繼續向下執行,這時候就該執行如下代碼:

springmvc工作流程_SpringMVC執行流程(源碼分析)

從此方法可見

ha

對象是此時的handler對象,說明在執行handler對象之前執行了攔截器,這也是遵循了執行鍊的順序。繼續執行下去,将完成了請求參數對象的封裝和響應中Json字元串與對象的轉換後,傳回了一個mv對象。那麼mv對象是什麼呢?其實是在上面定義的ModelAndView對象。傳回mv對象後,繼續執行便執行到了如下重要的執行邏輯:

springmvc工作流程_SpringMVC執行流程(源碼分析)

其中在執行過程中,判斷并執行了攔截器的後置方法。執行完後置方法後,進行了一系列的判斷,就開始執行了

processDispatchResult(processdRequest, response, mappdeHandler, mv, dispatchException)

方法,該方法中攜帶了請求對象、響應對象、handler對象、ModelAndView對象等,進入到此方法源碼中,你會發現他進行了一系列的判斷,通過如下方法對ModelAndView對象進行了渲染:

springmvc工作流程_SpringMVC執行流程(源碼分析)

對ModelAndView對象進行渲染和視圖解析後,繼續跟進方法,因為勝利馬上就要來臨了。如下圖:

springmvc工作流程_SpringMVC執行流程(源碼分析)

繼續執行,就會發現它開始通過

resolveViewName

方法來解析視圖了。于是,就進入到了該方法,如下圖:

springmvc工作流程_SpringMVC執行流程(源碼分析)

首先,看到此方法的源碼,你可以發現,

viewResolvers

視圖解析器會解析ModelAndView對象,并傳回了一個View對象。後來View對象也會被一個名叫

render

的方法渲染,如下:

springmvc工作流程_SpringMVC執行流程(源碼分析)

可見,此View對象并不簡單,它執行了一番過後,由于我的網頁跳轉時使用的請求轉發,于是就到了如下頁面源碼:

springmvc工作流程_SpringMVC執行流程(源碼分析)

點選此方法就會發現我們熟悉的請求轉發了,此時它在這裡讀取解析了

spring-mvc.xml

配置檔案,為内部預設的請求轉發拼接好了路徑

forward:/XXX/XXX

(此時也解析了spring-mvc.xml配置檔案内的其他元件),如下圖:

springmvc工作流程_SpringMVC執行流程(源碼分析)

如果是重定向呢,那麼就是如下類中的重定向方法,如下圖所示:

springmvc工作流程_SpringMVC執行流程(源碼分析)

随後,轉發或重定向跳轉至JSP頁面(視圖層)後,渲染資料到HTML中,并渲染完HTML内容後,輸出給浏覽器并作出響應,在浏覽器中顯示!

此SpringMVC我以打斷點調試的方式走了一遍底層的執行流程。我相信你自己打斷點調試也會有一個不錯的收獲!

SpringMVC的内部元件

  • HandlerMapping(處理器映射器)
  • HandlerAdapter(處理器擴充卡)
  • ViewResolver(視圖解析器)

SpringMVC預設元件初始化加載

上面我們通過Debug簡單的走了一遍SpringMVC的執行流程,但是前面所說的那麼多内部元件是怎麼來的呢?于是,我從DispatherServlet找到了一個方法

initStrategies

,如下:

springmvc工作流程_SpringMVC執行流程(源碼分析)

在執行流程開始之前,做了内部元件的一系列初始化操作,這裡我們以

initHandlerMappings

方法進行追溯,找到 SpringMVC 的預設配置檔案。進入 initHandlerMappings 方法,因為我們并沒有進行配置(注解或者 Bean 标簽),是以該方法中的前兩種情況都會跳過,會來到最下面的預設情況處,調用了 getDefaultStrategies 方法,讀取預設的配置檔案。

springmvc工作流程_SpringMVC執行流程(源碼分析)

在 getDefaultStrategies 方法中,有一個 defaultStrategies,我們當該類上面看一下,如下圖:

springmvc工作流程_SpringMVC執行流程(源碼分析)

這裡就是進行加載預設配置檔案的地方,點選

DEFAULT_STRATEGIES_PATH

常量,找到了預設的配置配置檔案。

springmvc工作流程_SpringMVC執行流程(源碼分析)

于是我想辦法翻到了這個配置檔案,裡面就初始化了各種元件,大家可以查閱:

springmvc工作流程_SpringMVC執行流程(源碼分析)

搜尋關注微信公衆号 碼出Offer 領取各種學習資料!

繼續閱讀