工作原理
上面的是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並返回給客戶端。
工作機制是什麼
Control的呼叫(續)
接著對於(二)的補充:主要是小結下Control的處理邏輯的關鍵操作;
對於control的處理關鍵就是:DispatcherServlet的handlerMappings集合中根據請求的URL匹配每一個handlerMapping物件中的某個handler,匹配成功之後將會返回這個handler的處理連線handlerExecutionChain物件。而這個handlerExecutionChain物件中將會包含使用者自定義的多個handlerInterceptor物件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found */ protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this .handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'" ); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null ) { return handler; } } return null ; } |
而對於handlerInterceptor介面中定義的三個方法中,preHandler和postHandler分別在handler的執行前和執行後執行,afterCompletion在view渲染完成、在DispatcherServlet返回之前執行。
PS:這麼我們需要注意的是:當preHandler返回false時,當前的請求將在執行完afterCompletion後直接返回,handler也將不會執行。
在類HandlerExecutionChain中的getHandler()方法是返回object物件的;
1 2 3 4 5 6 7 | /** * Return the handler object to execute. * @return the handler object */ public Object getHandler() { return this .handler; } |
這裡的handler是沒有型別的,handler的型別是由handlerAdapter決定的。dispatcherServlet會根據handler物件在其handlerAdapters集合中匹配哪個HandlerAdapter例項支援該物件。接下來去執行handler物件的相應方法了,如果該handler物件的相應方法返回一個ModelAndView物件接下來就是去執行View渲染了。
1 2 3 4 5 6 7 | /** * Return the handler object to execute. * @return the handler object */ public Object getHandler() { return this .handler; } |
---------------------------------------邪惡的分割線---------------------------------------------
Model設計
如果handler兌現返回了ModelAndView物件,那麼說明Handler需要傳一個Model例項給view去渲染模版。除了渲染頁面需要model例項,在業務邏輯層通常也有Model例項。
ModelAndView物件是連線業務邏輯層與view展示層的橋樑,對spring MVC來說它也是連線Handler與view的橋樑。ModelAndView物件顧名思義會持有一個ModelMap物件和一個View物件或者View的名稱。ModelMap物件就是執行模版渲染時候所需要的變數對應的例項,如jsp的通過request.getAttribute(String)獲取的JSTL標籤名對應的物件。velocity中context.get(String)獲取$foo對應的變數例項。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class ModelAndView { /** View instance or view name String */ private Object view; /** Model Map */ private ModelMap model; /** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */ private boolean cleared = false ; ..... } |
ModelMap其實也是一個Map,Handler中將模版中需要的物件存在這個Map中,然後傳遞到view對應的ViewResolver中。
1 2 3 4 | public interface ViewResolver { View resolveViewName(String viewName, Locale locale) throws Exception; } |
不同的ViewResolver會對這個Map中的物件有不同的處理方式;
- velocity中將這個Map儲存到VelocityContext中。
- JSP中將每一個ModelMap中的元素分別設定到request.setAttribute(modelName,modelValue);
-----------------------邪惡的分割線-----------------------------------------------
view設計
在spring MVC中,view模組需要兩個元件來支援:RequestToViewNameTranslator和ViewResolver
1 2 3 4 5 6 7 8 9 10 11 12 | public interface RequestToViewNameTranslator { /** * Translate the given {@link HttpServletRequest} into a view name. * @param request the incoming {@link HttpServletRequest} providing * the context from which a view name is to be resolved * @return the view name (or <code>null</code> if no default found) * @throws Exception if view name translation fails */ String getViewName(HttpServletRequest request) throws Exception; } |
對於 ViewResolver,前面有寫到了,就不寫了;
-----------------------邪惡的分割線-------------------------------------------------
RequestToViewNameTranslator:主要支援使用者自定義對viewName的解析,如將請求的ViewName加上字首或者字尾,或者替換成特定的字串等。
ViewResolver:主要是根據使用者請求的viewName建立適合的模版引擎來渲染最終的頁面,ViewResolver會根據viewName建立一個view物件,呼叫view物件的Void render方法渲染出頁面;
1 2 3 | public interface View { void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception; } |
下面來總結下 Spring MVC解析View的邏輯:
- dispatcherServlet方法呼叫getDefaultViewName()方法;
1 2 3 4 5 6 7 8 9 | /** * Translate the supplied request into a default view name. * @param request current HTTP servlet request * @return the view name (or <code>null</code> if no default found) * @throws Exception if view name translation failed */ protected String getDefaultViewName(HttpServletRequest request) throws Exception { return this .viewNameTranslator.getViewName(request); } |
- 呼叫了RequestToViewNameTranslator的getViewName方法;
1 2 3 4 5 6 7 8 9 10 11 12 | public interface RequestToViewNameTranslator { /** * Translate the given {@link HttpServletRequest} into a view name. * @param request the incoming {@link HttpServletRequest} providing * the context from which a view name is to be resolved * @return the view name (or <code>null</code> if no default found) * @throws Exception if view name translation fails */ String getViewName(HttpServletRequest request) throws Exception; } |
- 呼叫LocaleResolver介面的resolveLocale方法;
1 | Locale resolveLocale(HttpServletRequest request); |
- 呼叫ViewResolver介面的resolveViewName方法,返回view物件
1 | View resolveViewName(String viewName, Locale locale) throws Exception; |
- 呼叫render方法渲染出頁面