Spring MVC中的HandlerMapping與HandlerAdapter,springmvcmapping
Spring MVC中的HandlerMapping與HandlerAdapter的關係
最近和同事討論一個spring mvc的問題,問到HandlerMapping與HandlerAdapter有什麼關係?雖然使用spring mvc時間也不短,但是瞬間能起來的只有兩個關鍵詞:
- @RequestMapping,這個經常用的,每個 Controller下面的action方法上一般都會定義一個特有的url路徑。當HTTP請求請求發送到服務端後會根據url來尋找應該執行哪個Controller下面的哪個action,我理解為url與java代碼的一個路由關係。
@RequestMapping(value = "/bss/{priceId}", method = RequestMethod.GET) public ValueResult<ProductPrice> getProductPrice(HttpServletRequest request, @Min(value = 1,message = "priceId不合法") @PathVariable final long priceId) { //省略 }
- HandlerInterceptor,這個也是經常用的,做請求攔截時比較常用。
上面兩個關鍵詞儘管與問題有所關聯,但很明顯不是主要的,核心還是這兩個介面都是做什麼的,兩者之間有什麼互動。於是我們可以從一個請求開始調試下spring mvc的調用過程,以此來分析它們的作用以及關係。
Spring MVC配置兩個設定檔:
- 應用程式層級的applicationContext.xml,一般載入非web的配置,比如資料庫配置,redis配置等等。
- web層級的mvc-dispatcher-servlet.xml,這裡專註mvc的配置。
XmlWebApplicationContext context = new XmlWebApplicationContext();context.setConfigLocations(new String[]{"classpath*:applicationContext.xml","classpath*:spring/mvc-dispatcher-servlet.xml"});ServletContextHandler spingMvcHandler = new ServletContextHandler();spingMvcHandler.setContextPath(appConfig.getContext());spingMvcHandler.addEventListener(new ContextLoaderListener(context));spingMvcHandler.addServlet(new ServletHolder(new DispatcherServlet(context)), "/*");
這裡引用《張開濤》同學的圖來說明上面兩個配置的作用以及關係:
兩個核心類
- ContextLoaderListener
這的作用主要是在啟動web容器時載入ApplicationContext的資訊,用來建立ROOT ApplicationContext的,可以接收XML類型的,比如XmlWebApplicationContext,它將從XML設定檔中載入配置資訊。
這篇它不是重點至此主止。
- DispatcherServlet
也叫前端控制器,它是Spring MVC的統一訪問入口,負責職責的分配以及工作調試,由於它的功能複雜這裡只關心與HandlerMapping與HandlerAdaper的內容。下面是初始化的功能,其中有初始化HandlerMapping與HandlerAdaper。
@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */protected void initStrategies(ApplicationContext context) {//其它初始化initHandlerMappings(context);initHandlerAdapters(context);//其它初始化}
initHandlerMappings,主要是調用BeanFactoryUtils.beansOfTypeIncludingAncestors,其中一種非常重要的HandlerMapping是RequestMappingHandlerMapping,我們通過在Controller方面上加@RequestMapping注釋來配合使用,系統會將我們配置的RequestMapping資訊註冊到其中,詳細資料參數此圖:mappingRegistry中包含了所有的請求路由資訊。
代碼如下:
private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());// We keep HandlerMappings in sorted order.AnnotationAwareOrderComparator.sort(this.handlerMappings);}}//不載入全部的先省略//載入預設的邏輯先省略}
DispatcherServlet核心方法:doDispatch,三個重要步驟:
- getHandler,擷取頁面處理器,通俗點就是擷取由哪個Controller來執行,包含方法資訊以及方法參數等資訊。
- getHandlerAdapter,擷取HandlerAdapter,它包含一個handle方法,負責調用真實的頁面處理器進行請求處理並返回一個ModelAndView。HandlerAdpter裡面有一些常見的處理,比如訊息轉移,參數處理等,詳見此圖:裡面的argumentResolvers可以用來處理請求的參數,messageConverts是作訊息轉換等等。
- HandlerAdapter.handle,執行真實頁面處理器的處理請求。
請求時序圖(只關注HandlerMapping與HandlerAdapter)
doDispath擷取頁面處理器,然後根據頁面處理器擷取對應的HanlerAdapter,最後由HanlerAdaper來調用頁面處理器的方法。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//初始化省略try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//其它邏輯省略// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//其它邏輯省略}catch (Exception ex) {dispatchException = ex;}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}//異常邏輯省略}
具體的調用邏輯比較複雜,只選取與HandlerMapping與HandlerAdaper的部分,時序圖圖如下:
引用
http://jinnianshilongnian.iteye.com/blog/1602617