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;
}