最近和同事討論一個(gè)spring mvc的問題,問到HandlerMapping與HandlerAdapter有什么關(guān)系?雖然使用spring mvc時(shí)間也不短,但是瞬間能起來的只有兩個(gè)關(guān)鍵詞:
@RequestMapping,這個(gè)經(jīng)常用的,每個(gè) Controller下面的action方法上一般都會(huì)定義一個(gè)特有的url路徑。當(dāng)HTTP請(qǐng)求請(qǐng)求發(fā)送到服務(wù)端后會(huì)根據(jù)url來查找應(yīng)該執(zhí)行哪個(gè)Controller下面的哪個(gè)action,我理解為url與java代碼的一個(gè)路由關(guān)系。
@RequestMapping(value = "/bss/{priceId}", method = RequestMethod.GET) public ValueResult<ProductPrice> getProductPrice(HttpServletRequest request, @Min(value = 1,message = "priceId不合法") @PathVariable final long priceId) { //省略 }
HandlerInterceptor,這個(gè)也是經(jīng)常用的,做請(qǐng)求攔截時(shí)比較常用。
上面兩個(gè)關(guān)鍵詞盡管與問題有所關(guān)聯(lián),但很明顯不是主要的,核心還是這兩個(gè)接口都是做什么的,兩者之間有什么互動(dòng)。于是我們可以從一個(gè)請(qǐng)求開始調(diào)試下spring mvc的調(diào)用過程,以此來分析它們的作用以及關(guān)系。
Spring MVC配置
兩個(gè)配置文件:
應(yīng)用程序級(jí)別的applicationContext.xml,一般加載非web的配置,比如數(shù)據(jù)庫(kù)配置,redis配置等等。
web級(jí)別的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)), "/*");
這里引用《張開濤》同學(xué)的圖來說明上面兩個(gè)配置的作用以及關(guān)系:
兩個(gè)核心類
ContextLoaderListener
這的作用主要是在啟動(dòng)web容器時(shí)加載ApplicationContext的信息,用來創(chuàng)建ROOT ApplicationContext的,可以接收XML類型的,比如XmlWebApplicationContext,它將從XML配置文件中加載配置信息。
這篇它不是重點(diǎn)至此主止。
DispatcherServlet
也叫前端控制器,它是Spring MVC的統(tǒng)一訪問入口,負(fù)責(zé)職責(zé)的分配以及工作調(diào)試,由于它的功能復(fù)雜這里只關(guān)心與HandlerMapping與HandlerAdaper的內(nèi)容。下面是初始化的功能,其中有初始化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,主要是調(diào)用BeanFactoryUtils.beansOfTypeIncludingAncestors,其中一種非常重要的HandlerMapping是RequestMappingHandlerMapping,我們通過在Controller方面上加@RequestMapping注釋來配合使用,系統(tǒng)會(huì)將我們配置的RequestMapping信息注冊(cè)到其中,詳細(xì)數(shù)據(jù)參數(shù)此圖:mappingRegistry中包含了所有的請(qǐng)求路由信息。
代碼如下:
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); } } //不加載全部的先省略 //加載默認(rèn)的邏輯先省略 }
DispatcherServlet核心方法:doDispatch,三個(gè)重要步驟:
getHandler,獲取頁面處理器,通俗點(diǎn)就是獲取由哪個(gè)Controller來執(zhí)行,包含方法信息以及方法參數(shù)等信息。
getHandlerAdapter,獲取HandlerAdapter,它包含一個(gè)handle方法,負(fù)責(zé)調(diào)用真實(shí)的頁面處理器進(jìn)行請(qǐng)求處理并返回一個(gè)ModelAndView。HandlerAdpter里面有一些常見的處理,比如消息轉(zhuǎn)移,參數(shù)處理等,詳見此圖:里面的argumentResolvers可以用來處理請(qǐng)求的參數(shù),messageConverts是作消息轉(zhuǎn)換等等。
HandlerAdapter.handle,執(zhí)行真實(shí)頁面處理器的處理請(qǐng)求。
請(qǐng)求時(shí)序圖(只關(guān)注HandlerMapping與HandlerAdapter)
doDispath獲取頁面處理器,然后根據(jù)頁面處理器獲取對(duì)應(yīng)的HanlerAdapter,最后由HanlerAdaper來調(diào)用頁面處理器的方法。
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); } //異常邏輯省略 }
具體的調(diào)用邏輯比較復(fù)雜,只選取與HandlerMapping與HandlerAdaper的部分,時(shí)序圖圖如下: