javascript
Spring MVC中的二三事
HandlerMapping和HandlerAdapter
這個兩個組件應該算是spring mvc中最重要的幾個組件之一了,當一個請求到達DispatcherSerlvet后,spring mvc就全靠這各兩個組件定位并調用我們定義的Controller函數。是的,他們的功能就分別對應了“定位”和“調用”。
HandlerMapping
先看看該接口的申明:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | publicinterface HandlerMapping { ??// ... ??// 其他常量定義 ??/** ??* Return a handler and any interceptors for this request. The choice may be made ??* on request URL, session state, or any factor the implementing class chooses. ??* <p>The returned HandlerExecutionChain contains a handler Object, rather than ??* even a tag interface, so that handlers are not constrained in any way. ??* For example, a HandlerAdapter could be written to allow another framework's ??* handler objects to be used. ??* <p>Returns {@code null} if no match was found. This is not an error. ??* The DispatcherServlet will query all registered HandlerMapping beans to find ??* a match, and only decide there is an error if none can find a handler. ??* @param request current HTTP request ??* @return a HandlerExecutionChain instance containing handler object and ??* any interceptors, or {@code null} if no mapping found ??* @throws Exception if there is an internal error ??*/ ??HandlerExecutionChain getHandler(HttpServletRequest request) throwsException; } | 
實際干事的就只有getHandler一個方法,根據http請求確定將要被執行的執行鏈HandlerExecutionChain。一個HandlerExecutionChain就是由目標handler和一組HandlerInterceptor組成。但是需要注意的是,HandlerExecutionChain并不負責真正的執行動作,它也不知道如何去執行目標handler,而僅僅是一個保存這些對象的容器罷了。
目標的handler是Object類型,換句話說spring沒有提供任何接口來限定,可以是任何類型。因此真正的執行動作會發生在HandlerAdpater中,也就是說如果每個HandlerMapping(不管是spring提供的還是你自己寫的)都需要有對應的HandlerAdpater,當然不一定是一一對應有些是可以復用的。
如何定義HandlerInterceptor
不像目標handler,handler執行鏈上的攔截器是有限定類型的,也就是上面提到的HandlerInterceptor。那么如何配置這些HandlerInterceptor呢?
首先需要明確的是interceptor最終都會被配置到容器中使用的HandlerMapping組件中去,因為HandlerMapping會產生HandlerExecutionChain,需要將所有的interceptor一并設置到返回的HandlerExecutionChain中。那么最直接的方式就是在定義HandlerMapping的地方將需要的interceptor直接注入到對應的HandlerMapping類中,實際上該字段是聲明在AbstractHandlerMapping中,因此所有的HandlerMapping最好直接從AbstractHandlerMapping抽象類上繼承,而不要直接實現HandlerMapping接口。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <beans> ????<bean id="handlerMapping" ??????????class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMap ????????<property name="interceptors"> ????????????<list> ????????????????<ref bean="officeHoursInterceptor"/> ????????????</list> ????????</property> ??????<property name="interceptors"> ????????????????????<list> ????????????????<ref bean="officeHoursInterceptor"/> ????????????</list> ????????</property> ????</bean> ????<bean id="officeHoursInterceptor" ??????????class="samples.TimeBasedAccessInterceptor"> ????????<property name="openingTime"value="9"/> ????????<property name="closingTime"value="18"/> ????</bean> <beans> | 
實際上在spring中HandlerInterceptor有兩類,一類是名符其實的實現了HandlerInterceptor接口的類;另外一類是MappedInterceptor,顧名思義它除了HandlerInterceptor的功能外還有了path match的能力,實際上它就是包含了一個真正的HandlerInterceptor外加一些路徑匹配表達式。它的作用除了能夠讓spring調用其中包含的HandlerInterceptor之外,還具有路徑匹配的功能,也就是說會告訴spring只有當指定request的請求路徑復合要求的時候才會調用該interceptor。
OK,在回到配置HandlerInterceptor的第二種方法,就是使用<mvc:interceptors/>標簽,如下:
| 1 2 3 4 5 6 7 8 | <mvc:interceptors> ??<beanclass="my.MyInterceptor"/> ??<ref bean="interceptorRef"/> ??<mvc:interceptor> ??????<mvc:mapping path="/interceptor/*"/> ??????<beanclass="my.MyInterceptor1"/> ??</mvc:interceptor> </mvc:interceptors> | 
這個例子就定義了三個interceptor,分別通過bean, ref, interceptor子元素。其中bean和ref定義的interceptor會匹配任何request(因為沒有指定mapping path);使用interceptor子元素就可以指定mapping path了,那么它所表示的HandlerInterceptor就會根據request path來決定是否要執行。這些標簽都會被轉變為前面提到的MappedInterceptor。
前面說了HandlerInterceptor會最終被應用到HandlerMapping中,那通過xml配置的interceptor呢?實際上他們會被同時自動配置到spring容器中定義的所有HandlerMapping中,這也是最合乎情理的,因為你并不需要同時考慮你根據path所配置的interceptor到底應該作用到那個HandlerMapping中。相反,所有的HandlerMapping在擁有了這些MappedInterceptor后,在準備HandlerExecutionChain時就會根據當前的request path來決定要把哪些MappedInterceptor放進去,當然所有直接定義的HandlerInterceptor都會被放入chain中。
那么spring是怎么把這些MappedInterceptor放入到HandlerMapping中的呢?實際上spring僅僅是把他們定義到容器中,在HandlerMapping初始化的時候通過調用AbstractHandlerMapping.detectMappedInterceptors方法來自動發現所有的MappedInterceptor,并做一些必要的初始化配置。
另外一點,如果你使用了<mvc:annotation-driven/>的話,默認是會添加一個MappedInterceptor到容器中,這個interceptor是ConversionServiceExposingInterceptor,它會把<mvc:annotation-driven/>檢測或者創建的conversionService添加到HttpServletRequest的一個屬性中,以便整個http request處理流程可以隨時享用這個conversionService。因為并不是所有的組件都有享受spring ioc的能力,比如jsp tag,因此放在HttpServletRequest會比較方便。
HandlerAdapter
如何配置
HandlerAdapter是spring mvc中的獨立組件,因此和其他核心組件一樣可以通過一些三種方法獲得:
注意,2和3會disable掉1,但是2和3又會同時起作用。
應用流程
還是先看接口聲明吧:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | publicinterface HandlerAdapter { ??/** ??* Given a handler instance, return whether or not this {@code HandlerAdapter} ??* can support it. Typical HandlerAdapters will base the decision on the handler ??* type. HandlerAdapters will usually only support one handler type each. ??* <p>A typical implementation: ??* <p>{@code ??* return (handler instanceof MyHandler); ??* } ??* @param handler handler object to check ??* @return whether or not this object can use the given handler ??*/ ??booleansupports(Object handler); ??/** ??* Use the given handler to handle this request. ??* The workflow that is required may vary widely. ??* @param request current HTTP request ??* @param response current HTTP response ??* @param handler handler to use. This object must have previously been passed ??* to the {@code supports} method of this interface, which must have ??* returned {@code true}. ??* @throws Exception in case of errors ??* @return ModelAndView object with the name of the view and the required ??* model data, or {@code null} if the request has been handled directly ??*/ ??ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throwsException; ??/** ??* Same contract as for HttpServlet's {@code getLastModified} method. ??* Can simply return -1 if there's no support in the handler class. ??* @param request current HTTP request ??* @param handler handler to use ??* @return the lastModified value for the given handler ??* @see javax.servlet.http.HttpServlet#getLastModified ??* @see org.springframework.web.servlet.mvc.LastModified#getLastModified ??*/ ??longgetLastModified(HttpServletRequest request, Object handler); } | 
DispatherServlet在通過前面的HandlerMapping獲得了當前請求的HandlerExecutionChain之后,就會哪些chain里面定義的目標handler遍歷所有配置好的HandlerAdapter,并調用supports方法詢問不同的adapter是否可以處理,如果可以就進入處理流程,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 | protectedHandlerAdapter getHandlerAdapter(Object handler) throwsServletException { ??for(HandlerAdapter ha : this.handlerAdapters) { ??????if(logger.isTraceEnabled()) { ??????????logger.trace("Testing handler adapter [" + ha + "]"); ??????} ??????if(ha.supports(handler)) { ??????????returnha; ??????} ??} ??thrownew ServletException("No adapter for handler [" + handler + ??????????"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); } | 
處理流程如下:
一個栗子
邏輯就是這么簡單,沒有什么好多說的,因為就像前面說的不同的HandlerAdapter是需要配合不同的HandlerMapping產生的目標handler,沒有固定的規律和模式。就拿SimpleControllerHandlerAdapter這個例子來說明下把。可以和它配對的HandlerMapping有ControllerBeanNameHandlerMapping和ControllerClassNameHandlerMapping,或者說從AbstractControllerUrlHandlerMapping繼承下來的類。
先看看SimpleControllerHandlerAdapter:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | publicclass SimpleControllerHandlerAdapter implementsHandlerAdapter { ??@Override ??publicboolean supports(Object handler) { ??????return(handler instanceofController); ??} ??@Override ??publicModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) ??????????throwsException { ??????return((Controller) handler).handleRequest(request, response); ??} ??@Override ??publiclong getLastModified(HttpServletRequest request, Object handler) { ??????if(handler instanceofLastModified) { ??????????return((LastModified) handler).getLastModified(request); ??????} ??????return-1L; ??} } | 
可以得出以下簡單的結論:
從中我們可以斷定和它配合的HandlerMapping返回的目標handler必須是Controller類型。好吧,我們來看看ControllerBeanNameHandlerMapping和ControllerClassNameHandlerMapping是干什么的。他們兩個實際上是非常相似的,共同的父類都會掃描容器中所有定義的bean,如果該bean是Controller類型,那么就交給這兩個不同的子類做處理來決定如何將這個Controller加入到mapping中。
那么在收到請求后,這兩個HandlerMapping會根據request path匹配已經保存的mapping數據,如果找到匹配的就會將之前存好的這個bean,也就是這個Controller對象當做目標handler返回出去。在后面的調用流程中自然就可以被SimpleControllerHandlerAdapter處理了。
當然,一個請求來了具體被那個HandlerMapping處理要看不同HandlerMapping的處理能力,還處理順序,自己不能處理的舊交由下一個處理,其順序是HandlerMapping的order值確定的。
這僅僅是一個例子,目前Controller類已經不推薦使用了,更多的請使用annotation的方法,當然其對應的處理組件是RequestMappingHandlerMapping和RequestMappingHandlerAdapter。
 
 
 原文出處:?shenzhang
 
from:?http://www.importnew.com/22188.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Spring MVC中的二三事的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 图解GitHub和SourceTree入
- 下一篇: exVim安装
