日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring MVC中的二三事

發布時間:2025/3/21 javascript 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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中的獨立組件,因此和其他核心組件一樣可以通過一些三種方法獲得:

  • DispatcherServlet.peroperties默認提供
  • <mvc:annotation-driven/>提供
  • 自己配置在spring配置文件中
  • 注意,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"); }

    處理流程如下:

  • 如果是GET或者HEAD請求,調用HandlerAdapter.getLastModified方法看看目標Controller方法在對于該請求有沒有可用的lastModified邏輯,如果有的話就使用ServletWebRequest.checkNotModified邏輯判斷當前lastModfied值和http header的上次緩存值,如果還沒有過期就設置304頭并且返回并結束整個請求流程。否則繼續。
  • 應用preHandle方法,調用所有的HandlerInterceptor.preHandle方法
  • 調用HandlerAdapter.handle方法進行目標handler的調用(調用controller),得到ModelAndView返回值
  • 應用interceptor.postHandle方法。
  • 最后根據handle返回值的請求調用DispatcherServlet.processDispatchResult方法來根據返回值類型處理成最終的http response。
  • 一個栗子

    邏輯就是這么簡單,沒有什么好多說的,因為就像前面說的不同的HandlerAdapter是需要配合不同的HandlerMapping產生的目標handler,沒有固定的規律和模式。就拿SimpleControllerHandlerAdapter這個例子來說明下把??梢院退鋵Φ腍andlerMapping有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; ??} }

    可以得出以下簡單的結論:

  • 它只能處理目標handler是Controller類型(實現了Controller接口)的調用
  • 對于lastModified特性,如果目標handler(從1可知肯定是一個Controller類型)也實現了LastModifed接口,那么就調用該接口的getLastMofied函數來得到lastMofiy值,否則返回-1表示不支持。
  • 調用過程非常簡單,就是調用目標Controller的handleRequest方法。
  • 從中我們可以斷定和它配合的HandlerMapping返回的目標handler必須是Controller類型。好吧,我們來看看ControllerBeanNameHandlerMapping和ControllerClassNameHandlerMapping是干什么的。他們兩個實際上是非常相似的,共同的父類都會掃描容器中所有定義的bean,如果該bean是Controller類型,那么就交給這兩個不同的子類做處理來決定如何將這個Controller加入到mapping中。

  • 對于ControllerBeanNameHandlerMapping,它會把這個bean的名字及其alias作為request path
  • 對于ControllerClassNameHandlerMapping,它會把這個bean的類名作為request path,比如HelloController對應為”/hello”。
  • 那么在收到請求后,這兩個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中的二三事的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。