最近和同事討論一個spring mvc的問題,問到HandlerMapping與HandlerAdapter有什麼關系?雖然使用spring mvc時間也不短,但是瞬間能起來的只有兩個關鍵詞:
@RequestMapping(value = "/bss/{priceId}", method = RequestMethod.GET) public ValueResult<ProductPrice> getProductPrice(HttpServletRequest request, @Min(value = 1,message = "priceId不合法") @PathVariable final long priceId) { //省略 }
上面兩個關鍵詞盡管與問題有所關聯,但很明顯不是主要的,核心還是這兩個接口都是做什麼的,兩者之間有什麼互動。於是我們可以從一個請求開始調試下spring 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)), "/*");
這裡引用《張開濤》同學的圖來說明上面兩個配置的作用以及關系:
這篇它不是重點至此主止。
@Override protected 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,三個重要步驟:
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的部分,時序��圖如下: