天天看點

SpringMVC 的工作機制

在一個工程中如果想要使用 SpringMVC的話,隻需要兩個步驟

在web.xml中配置一個DispatcherServlet。需要配置一個org.springframework.web.servlet.DispatcherServlet的servlet。

定義一個dispatcherServlet-servlet.xml配置檔案。在這個配置檔案裡面我們隻需要擴充一個路徑映射關系,定義一個視圖解析器,再定義一個業務邏輯的處理流程規則。

這樣就可以搞定一個最基本的Spring MVC的應用了。

對于spring MVC架構中,有三個元件是使用者必須定義和擴充的:

    定義URL映射規則:handlerMapping

    實作業務邏輯的handler執行個體對象:handlerAdapter

    渲染模版資源:ViewResolver

下面是傳統的配置示例:

下面是用freemaker引擎的配置示例:

SpringMVC的入口是DispatchServlet,它的工作大緻可以分為兩個部分,一個是初始化,另外一個是請求處理。

SpringMVC 的工作機制
SpringMVC 的工作機制

要做的8件事是如下。

initMultipartResolver:初始化MultipartResolver,用于處理檔案上傳服務,如果有檔案上傳,那麼會将目前的HttpServletRequest包裝成DefaultMultipartHttp ServletRequest,并且将每個上傳的内容封裝成CommonsMultipartFile對象。

initLocaleResolver:用于處理應用的國際化問題,通過解析請求的Locale和設定響應的Locale來控制應用中的字元編碼問題。

initThemeResolver:用于定義一個主題,例如,可以根據使用者的喜好來設定使用者通路的頁面的樣式,可以将這個樣式作為一個Theme Name儲存,用于請求的Cookie中或者儲存在服務端的Session中,以後每次請求根據這個Theme Name來傳回特定的内容。

initHandlerMappings:用于定義使用者設定的請求映射關系,例如,前面示例中的SimpleUrlHandlerMapping把用于使用者請求的URL映射成一個個Handler執行個體。HandlerMapping必須定義,如果沒有定義,将擷取DispatcherServlet.properties檔案中預設的兩個HandlerMapping,分别是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping。

initHandlerAdapters:用于根據Handler的類型定義不同的處理規則,例如,定義SimpleControllerHandlerAdapter處理所有Controller的執行個體對象,在HandlerMapping中将URL映射成一個Controller執行個體,那麼Spring MVC在解析時SimpleController HandlerAdapter就會調用這個Controller執行個體。同樣HandlerAdapters也必須定義,如果沒有定義,将擷取DispatcherServlet.properties檔案中預設的四個Handler Adapters,分别是HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、ThrowawayControllerHandlerAdapter和AnnotationMethodHandlerAdapter。

initHandlerExceptionResolvers:當Handler處理出錯時,通過這個Handler來統一做處理,預設的實作類是SimpleMappingExceptionResolver,将錯誤日志記錄在log檔案中,并且轉到預設的錯誤頁面。

initRequestToViewNameTranslator:将指定的ViewName按照定義的RequestToVie wNameTranslator替換成想要的格式,如加上字首或者字尾等。

initViewResolvers:用于将View解析成頁面,在ViewResolvers中可以設定多個解析政策,如可以根據JSP來解析,或者按照Velocity模闆解析。預設的解析政策是InternalResourceViewResolver,按照JSP頁面來解析。

SpringMVC 的工作機制

上面的是springMVC的工作原理圖:

1、用戶端發出一個http請求給web伺服器,web伺服器對http請求進行解析,如果比對DispatcherServlet的請求映射路徑(在web.xml中指定),web容器将請求轉交給DispatcherServlet.

2、DipatcherServlet接收到這個請求之後将根據請求的資訊(包括URL、Http方法、請求封包頭和請求參數Cookie等)以及HandlerMapping的配置找到處理請求的處理器(Handler)。

3-4、DispatcherServlet根據HandlerMapping找到對應的Handler,将處理權交給Handler(Handler将具體的處理進行封裝),再由具體的HandlerAdapter對Handler進行具體的調用。

5、Handler對資料處理完成以後将傳回一個ModelAndView()對象給DispatcherServlet。

6、Handler傳回的ModelAndView()隻是一個邏輯視圖并不是一個正式的視圖,DispatcherSevlet通過ViewResolver将邏輯視圖轉化為真正的視圖View。

7、Dispatcher通過model解析出ModelAndView()中的參數進行解析最終展現出完整的view并傳回給用戶端。

HandlerMapping初始化:

将URL與Handler的對應關系儲存在HandlerMapping集合中,并将所有的interceptors對象儲存在adaptedInterceptors數組中,等請求到來的時候執行所有的adaptedIntercoptors數組中的interceptor對象。所有的interceptor必須實作HandlerInterceptor接口。

HandlerAdapter初始化:

Spring MVC中提供了如下三個典型的簡單HandlerAdapter實作類。

SimpleServletHandlerAdapter:可以繼承HttpRequestHandler接口,所有的Handler可以實作其void handleRequest(HttpServletRequest request, HttpServletResponse response)方法,這個方法沒有傳回值。

SimpleControllerHandlerAdapter:可以繼承Controller接口,所有的Handler可以實作其public ModelAndView handle(HttpServletRequest request, HttpServletRes ponse response, Object handler)方法,該方法會傳回ModelAndView對象,用于後續的模闆渲染。

SimpleServletHandlerAdapter:可以直接繼承Servlet接口,可以将一個Servlet作為一個Handler來處理這個請求。

Spring MVC的HandlerAdapter機制可讓Handler的實作更加靈活,不需要和其他架構那樣隻能和某個Handler接口綁定起來。

對于handlerAdapter的初始化沒有什麼特别之處,隻是簡單的建立一個handlerAdapter對象,将這個對象儲存在DispatcherServlet的HandlerAdapters集合中。當Spring MVC将某個URL對應到某個Handler時候,在handlerAdapters集合中查詢那個handlerAdapter對象supports這個Handler,handlerAdapter對象将會被傳回,然後調用這個handlerAdapter接口對應的方法。如果這個handlerAdapter對象是SimpleControllerHandlerAdapter,将調用Controller接口的public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)方法。

Control的調用邏輯:

整個Spring MVC的調用是從DispatcherServlet的doService方法開始的,在doService方法中會将ApplicationContext、localeResolver、themeResolver等對象添加到request中便于在後面使用,接着就調用doDispatch方法,這個方法是主要的處理使用者請求的地方。 

對于control的處理關鍵就是:DispatcherServlet的handlerMappings集合中根據請求的URL比對每一個handlerMapping對象中的某個handler,比對成功之後将會傳回這個handler的處理連接配接handlerExecutionChain對象。而這個handlerExecutionChain對象中将會包含使用者自定義的多個handlerInterceptor對象。

對于handlerInterceptor接口中定義的三個方法中,preHandler和postHandler分别在handler的執行前和執行後執行,afterCompletion在view渲染完成、在DispatcherServlet傳回之前執行。當preHandler傳回false時,目前的請求将在執行完afterCompletion後直接傳回,handler也将不會執行。

在類HandlerExecutionChain中的getHandler()方法是傳回object對象的;這裡的handler是沒有類型的,handler的類型是由handlerAdapter決定的。dispatcherServlet會根據handlerAdapters集合中第一個支援該handler對象的HandlerAdapter對象。接下來去執行handler對象的相應方法了,如果該handler對象的相應方法傳回一個ModelAndView對象(SampleControllerHandlerAdapter)接下來就是去執行View渲染了。

如果handler傳回了ModelAndView對象,那麼說明Handler需要傳一個Model執行個體給view去渲染模版。除了渲染頁面需要model執行個體,在業務邏輯層通常也有Model執行個體。

ModelAndView對象是連接配接業務邏輯層與view展示層的橋梁,對spring MVC來說它也是連接配接Handler與view的橋梁。ModelAndView對象顧名思義會持有一個ModelMap對象和一個View對象或者View的名稱(譬如hello.vm模闆名字)。ModelMap對象就是執行模版渲染時候所需要的變量對應的執行個體,如jsp的通過request.getAttribute(String)擷取的JSTL标簽名對應的對象。velocity中context.get(String)擷取$foo對應的變量執行個體。

ModelMap其實也是一個Map,Handler中将模版中需要的對象存在這個Map中,然後傳遞到view對應的ViewResolver中。

不同的ViewResolver會對這個Map中的對象有不同的處理方式;

velocity中将這個Map儲存到VelocityContext中。

freemarker模闆引擎來說将ModelMap包裝成freemarker.template. TemplateHash Model

JSP中将每一個ModelMap中的元素分别設定到request.setAttribute(modelName,modelValue);

在spring MVC中,view子產品需要兩個元件來支援:RequestToViewNameTranslator和ViewResolver

RequestToViewNameTranslator:主要支援使用者自定義對viewName的解析,如将請求的ViewName加上字首或者字尾,或者替換成特定的字元串等。

ViewResolver:主要是根據使用者請求的viewName建立适合的模版引擎來渲染最終的頁面,ViewResolver會根據viewName建立一個view對象,調用view對象的Void render方法渲染出頁面。

SpringMVC 的工作機制

UrlBasedViewResolver類實作了AbstractCachingViewResolver抽象類,通過設定ViewClass來建立View對象。如果使用FreeMarkerViewResolver類,會将ViewClass設定為FreeMarkerView.class;使用VelocityViewResolver類,會将ViewClass設定為VelocityView.class。InternalResourceViewResolver類可以通過注入的方式設定ViewClass屬性來初始化自定義的View對象。由于AbstractCachingViewResolver抽象類也繼承了WebApplicationObjectSupport,是以所有的AbstractCachingViewResolver子類可以通過覆寫initApplicationContext方法在Spring MVC架構啟動時完成初始化工作。如FreeMarkerViewResolver和VelocityViewResolver就是在啟動調用setViewClass方法時設定ViewClass屬性的。JSP的ViewResolver對應的是InternalResourceViewResolver類,當調用resolveViewName方法時會調用createView方法,将ViewClass屬性對應的InternalResourceView類執行個體化。最後調用InternalResourceView的render方法渲染出JSP頁面。

 Spring MVC解析View的邏輯:

dispatcherServlet調用getDefaultViewName()方法;

調用RequestToViewNameTranslator的getViewName方法;

調用LocaleResolver接口的resolveLocale方法;

調用ViewResolver接口的resolveViewName方法,傳回view對象

調用render方法渲染出頁面

SpringMVC 的工作機制

JSP的ViewResolver對應的是InternalResourceViewResolver類,當調用resolveViewName方法時會調用createView方法,将ViewClass屬性對應的InternalResourceView類執行個體化。最後調用InternalResourceView的render方法渲染出JSP頁面。

SpringMVC 的工作機制