歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Spring MVC中的HandlerMapping與HandlerAdapter

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。
@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,三個重要步驟:

  • 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的部分,時序��圖如下:

Copyright © Linux教程網 All Rights Reserved