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()方法
其中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)方法:
定義了判斷請求和指定 pattern 路徑是否匹配的接口方法,
那麼上面提及的HandlerMappings是在哪裏初始化的呢?
答案就在DispatcherServlet的initHandlerMappings()方法:
<3>處的getDefaultStrategies方法如下:
關於 defaultStrategies 屬性,涉及的代碼如下:
其中,DispatcherServlet.properties 的配置如下:
我們可以看到,HandlerMapping 接口,對應的是 BeanNameUrlHandlerMapping RequestMappingHandlerMapping 和 RouterFunctionMapping類。
然後,我們再回過頭看 DispatcherServlet 對 handlerMappings 的使用,在 #getHandler(HttpServletRequest request) 方法中,代碼如下:
假設有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()解析過程就可以了。
過濾器是在到達DispatcherServlet之前進行過濾;
攔截器是經過DispatcherServlet後,到達Controller之前進行攔截。
這一節,我們來看看HandlerAdapter,主要看一下它的兩個重要子類:AbstractHandlerMethodAdapter 和 RequestMappingHandlerAdapter
重點看一下HandlerAdapter執行處理器邏輯的具體流程:
入口:DispatcherServlet的doDispatch()方法
此處調用的是AbstractHandlerMethodAdapter#handle()方法:
調用子類RequestMappingHandlerAdapter#handleInternal()方法:
這裏,就是利用反射執行@RequestMapping註解的目標方法。
在 Spring MVC 中,可以使用 @RequestBody 和 @ResponseBody 兩個註解,分別完成請求報文到對象和對象到響應報文的轉換,底層這種靈活的消息轉換機制,就是Spring 3.x 中新引入的 HttpMessageConverter ,即消息轉換器機制。
這個類是 Spring MVC 內部對一次 Http 請求報文的抽象,在 HttpMessageConverter 的 read(...) 方法中,有一個 HttpInputMessage 的形參,它正是 Spring MVC 的消息轉換器所作用的受體“請求消息”的內部抽象,消息轉換器從“請求消息”中按照規則提取消息,轉換爲方法形參中聲明的對象。
這個類是 Spring MVC 內部對一次 Http 響應報文的抽象,在 HttpMessageConverter 的 #write(...) 方法中,有一個 HttpOutputMessage 的形參,它正是 Spring MVC 的消息轉換器所作用的受體“響應消息”的內部抽象,消息轉換器將“響應消息”按照一定的規則寫到響應報文中。
對消息轉換器最高層次的接口抽象,描述了一個消息轉換器的一般特徵,我們可以從這個接口中定義的方法,來領悟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的整個請求流程就基本上簡單分析完了。