Spring MVC 一個請求的完整過程

Spring MVC 一個請求的完整過程

 

 

整個過程如上圖,首先,用戶的瀏覽器發出了一個請求,這個請求經過互聯網到達了我們的服務器。Servlet 容器首先接待了這個請求,並將該請求委託給 DispatcherServlet 進行處理。接着 DispatcherServlet 將該請求傳給了處理器映射組件 HandlerMapping,並獲取到適合該請求的攔截器和處理器。在獲取到處理器後,DispatcherServlet 還不能直接調用處理器的邏輯,需要進行對處理器進行適配。處理器適配成功後,DispatcherServlet 通過處理器適配器 HandlerAdapter 調用處理器的邏輯,並獲取返回值 ModelAndView。之後,DispatcherServlet 需要根據 ModelAndView 解析視圖。解析視圖的工作由 ViewResolver 完成,若能解析成功,ViewResolver 會返回相應的視圖對象 View。在獲取到具體的 View 對象後,最後一步要做的事情就是由 View 渲染視圖,並將渲染結果返回給用戶。

流程入口:

org.springframework.web.servlet.FrameworkServlet#service()方法

最終調用DispatcherServlet.doDispatch()方法

HandlerMapping組件

入口:

其中RequestMappingHandlerMapping的類圖如下:

我們會發現,它繼承自抽象類ApplicationObjectSupport,而ApplicationObjectSupport實現了ApplicationContextAware接口,

重寫了setApplicationContext方法,如下:

最終調用了AbstractHandlerMapping的initApplicationContext()方法:

注:

<2>處的MappedInterceptor類型的Bean,指的就是xml文件裏<mvc: interceptors />標籤配置的Bean;

MappedInterceptor 攔截器類,會根據請求路徑做匹配,是否進行攔截。

在上一篇文章裏,我們講到DispatcherServlet.doDispatch()方法,其中第一步就是獲取適合當前請求的處理器和攔截器們:

這裏調用的就是AbstractHandlerMapping#getHandler()方法。

<5>處,調用的是getHandlerExecutionChain(Object handler, HttpServletRequest request)方法:

MatchableHandlerMapping

定義了判斷請求和指定 pattern 路徑是否匹配的接口方法,

目前實現該接口的類有兩個:

返回的是RequestMatchResult類:

那麼上面提及的HandlerMappings是在哪裏初始化的呢?

答案就在DispatcherServlet的initHandlerMappings()方法:

<3>處的getDefaultStrategies方法如下:

關於 defaultStrategies 屬性,涉及的代碼如下:

其中,DispatcherServlet.properties 的配置如下:

我們可以看到,HandlerMapping 接口,對應的是 BeanNameUrlHandlerMapping  RequestMappingHandlerMapping 和 RouterFunctionMapping類。

然後,我們再回過頭看 DispatcherServlet 對 handlerMappings 的使用,在 #getHandler(HttpServletRequest request) 方法中,代碼如下:

一切就很清晰了。。。

HandlerInterceptor

攔截器處理接口:

HandlerExecutionChain

下面來看攔截的幾個方法:

1. applyPreHandle

2. applyPostHandle

3. triggerAfterCompletion

根據以上的代碼,分析一下不同攔截器及其方法的執行順序:

假設有5個攔截器編號分別爲12345,若一切正常則方法的執行順序是12345的preHandle,54321的postHandle,54321的afterCompletion。

若編號3的攔截器的preHandle方法返回false或者拋出了異常,接下來會執行的是21的afterCompletion方法。這裏要注意的地方是,我們在寫一個

攔截器的時候要謹慎的處理preHandle中的異常,因爲這裏一旦有異常拋出就不會再受到這個攔截器的控制。12345的preHandle的方法執行過之後,

若handle過程出現了異常或者某個攔截器的postHandle方法出現異常,則接下來都會執行54321的afterCompletion方法,因爲只要12345的preHandle

方法執行完,當前攔截器的攔截器就會記錄成編號5的攔截器,而afterCompletion總是從當前的攔截器逆向的向前執行。

攔截器配置

1. xml配置文件方式(<mvc:interceptors/>標籤):

每一個<mvc:interceptor>都會被InterceptorsBeanDefinitionParser解析爲一個MappedInterceptor類型的Bean,註冊到IOC容器裏。

在 AbstractHandlerMapping 的 #detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) 方法中,會掃描 MappedInterceptor 類型的 Bean 。代碼如下:

所以這種方式,只要看一下InterceptorsBeanDefinitionParser類的parse()解析過程就可以了。

2. Java Config方式:

Spring Boot 時,這是主流的方式

注:過濾器和攔截器的區別

過濾器是在到達DispatcherServlet之前進行過濾;

攔截器是經過DispatcherServlet後,到達Controller之前進行攔截

這一節,我們來看看HandlerAdapter,主要看一下它的兩個重要子類:AbstractHandlerMethodAdapter 和 RequestMappingHandlerAdapter

重點看一下HandlerAdapter執行處理器邏輯的具體流程:

入口:DispatcherServlet的doDispatch()方法

此處調用的是AbstractHandlerMethodAdapter#handle()方法:

調用子類RequestMappingHandlerAdapter#handleInternal()方法:

這裏,就是利用反射執行@RequestMapping註解的目標方法。

HttpMessageConverter

在 Spring MVC 中,可以使用 @RequestBody 和 @ResponseBody 兩個註解,分別完成請求報文到對象和對象到響應報文的轉換,底層這種靈活的消息轉換機制,就是Spring 3.x 中新引入的 HttpMessageConverter ,即消息轉換器機制。

1)HttpInputMessage

這個類是 Spring MVC 內部對一次 Http 請求報文的抽象,在 HttpMessageConverter 的 read(...) 方法中,有一個 HttpInputMessage 的形參,它正是 Spring MVC 的消息轉換器所作用的受體“請求消息”的內部抽象,消息轉換器從“請求消息”中按照規則提取消息,轉換爲方法形參中聲明的對象。

2)HttpOutputMessage

這個類是 Spring MVC 內部對一次 Http 響應報文的抽象,在 HttpMessageConverter 的 #write(...) 方法中,有一個 HttpOutputMessage 的形參,它正是 Spring MVC 的消息轉換器所作用的受體“響應消息”的內部抽象,消息轉換器將“響應消息”按照一定的規則寫到響應報文中。

3)HttpMessageConverter

對消息轉換器最高層次的接口抽象,描述了一個消息轉換器的一般特徵,我們可以從這個接口中定義的方法,來領悟Spring3.x的設計者對這一機制的思考過程。

HttpMessageConverter接口的定義出現了成對的 canRead(...) + #read(...) 和 #canWrite(...) + #write(...) 方法。而 MediaType 是對請求的 Media Type 屬性的封裝。舉個例子,當我們聲明瞭下面這個處理方法。

在 SpringMVC 進入 #readString(...) 方法前,會根據 @RequestBody 註解選擇適當的 HttpMessageConverter 實現類來將請求參數解析到 string 變量中,具體來說是使用了 StringHttpMessageConverter 類,它的 #canRead(...) 方法返回 true,然後它的 #read(...) 方法會從請求中讀出請求參數,綁定到 #readString(@RequestBody String string) 方法的 string 變量中。

當 Spring MVC 執行 #readString(@RequestBody String string) 方法後,由於返回值標識了 @ResponseBody 註解,Spring MVC 將使用 StringHttpMessageConverter 的 #write(...) 方法,將結果作爲 String 值寫入響應報文,當然,此時 #canWrite(....) 方法返回 true 

我們可以用下面的圖,簡單描述一下這個過程。

4)RequestResponseBodyMethodProcessor

將上述過程集中描述的一個類是 org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor ,這個類同時實現了 HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler 兩個接口。前者是將請求報文綁定到處理方法形參的策略接口,後者則是對處理方法返回值進行處理的策略接口。兩個接口的源碼如下:

RequestResponseBodyMethodProcessor 這個類,同時充當了方法參數解析和返回值處理兩種角色。我們從它的源碼中,可以找到上面兩個接口的方法實現。

1)對 HandlerMethodArgumentResolver 接口的實現:

2)對 HandlerMethodReturnValueHandler 接口的實現:

看完上面的代碼,整個 HttpMessageConverter 消息轉換的脈絡已經非常清晰。因爲兩個接口的實現,分別是以是否有 @RequestBody 和 @ResponseBody 爲條件,然後分別調用 HttpMessageConverter 來進行消息的讀寫。

至此,SpringMVC的整個請求流程就基本上簡單分析完了。

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