1. 程式人生 > 其它 >SpringMVC-詳解HandlerMapping的前世今生之誕生

SpringMVC-詳解HandlerMapping的前世今生之誕生

技術標籤:spring mvcspringmvcHandler

本文基於spring 5.5.2.release

springmvc接收到請求後,第一步要做的事情就是查詢Handler,以確定是否可以處理該請求,Handler可以簡單的理解為Controller。這個查詢的過程是由HandlerMapping完成的。
HandlerMapping是一個介面,實現類必須實現如下方法:

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

springmvc提供了多種不同的HandlerMapping實現類,每個實現類都可以根據請求引數查詢對應的Handler。這些實現類例項化物件後都放到DispatcherServlet的屬性handlerMappings中,以供後續遍歷時查詢訪問。那這些物件是如何放到屬性handlerMappings中的?

文章目錄

一、初始化屬性handlerMappings

下面是DispatcherServlet的屬性handlerMappings的定義:

	/** List of HandlerMappings used by this servlet. */
	@Nullable
	private List<HandlerMapping> handlerMappings;

下面來看一下這個屬性是如何初始化的。
DispatcherServlet實現了Servlet介面,因此當第一次外部請求時,會觸發該類的init()方法,init()方法最終呼叫到initHandlerMappings():

	//入參context是web環境下的ApplicationContext物件,
	//預設實現類是AnnotationConfigServletWebServerApplicationContext
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
		//detectAllHandlerMappings預設是true,
		//表示是自動檢測,還是隻從容器中查詢名字為“handlerMapping”的物件
		if (this.detectAllHandlerMappings)
{ //下面開始自動查詢HandlerMapping物件 //下面這一行程式碼用於從容器中查詢型別為HandlerMapping的物件, //包括父容器也會一併查詢,scope=prototype型別的也會搜尋到 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings);//排序 } } else { try { //下面是從容器中查詢名字為“handlerMapping”且型別為HandlerMapping的物件 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. //如果上述過程沒有找到HandlerMapping,那麼讀取檔案DispatcherServlet.properties, //從該檔案中載入HandlerMapping物件 if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }

從上面程式碼可以看到springmvc直接從容器中獲取HandlerMapping物件新增到屬性handlerMappings中。那麼容器中的物件從哪來?答案是WebMvcConfigurationSupport。WebMvcConfigurationSupport提供了requestMappingHandlerMapping()、viewControllerHandlerMapping()等建立HandlerMapping的方法。該類一共建立四個,分別是:

  • RequestMappingHandlerMapping
  • BeanNameUrlHandlerMapping
  • RouterFunctionMapping
  • resourceHandlerMapping

除了上面四個,還有一個是在EnableWebMvcConfiguration中建立的:

  • WelcomePageHandlerMapping

有的HandlerMapping物件,指定了order屬性,這個屬性用於排序使用,可以看到initHandlerMappings()裡面也對這些物件做了排序,排序結果如下:
在這裡插入圖片描述
DispatcherServlet遍歷HandlerMapping便是以上圖的順序進行,當從其中一個HandlerMapping中找到合適的Handler,那麼後面的便不再遍歷,下面是遍歷的程式碼,DispatcherServlet便是呼叫下面這個方法找到合適的Handler:

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;//找到後直接返回
				}
			}
		}
		return null;
	}