標籤:deb log 擴充 turn type detail imp int err
HandlerMapping
首先這是一個介面,也就是可擴充。它的作用就是根據不同的請求去匹配對應的Handler,也就是根據請求匹配一個要求處理常式。這個過程需要兩個步驟:第一步,需要將Handler註冊到HandlerMapping中;第二步,分析請求根據規則從登入的Handler中匹配到對應的Handler,即Controller。預設情況下,SpringMvc為我們提供了幾個預設的HandlerMapping的實現,通過優先順序的次序決定執行的順序。
HandlerMapping執行順序
在基於Spring MVC的Web應用程式中,我們可以為DispatcherServlet提供多個Handler- Mapping供其使用。DispatcherServlet在選用HandlerMapping的過程中,將根據我們所指定的一系列HandlerMapping的優先順序進行排序,然後優先使用優先順序在前的HandlerMapping。如果當前的HandlerMapping能夠返回可用的Handler,DispatcherServlet則使用當前返回的Handler進行Web請求的處理,而不再繼續詢問其他的HandlerMapping。否則,DispatcherServlet將繼續按照各個HandlerMapping的優先順序進行詢問,直到擷取一個可用的Handler為止。
SimpleUrlHandlerMapping
現在,通過這個實作類別,我們來看看handlerMapping是如何註冊和擷取handler的。
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { private final Map<String, Object> urlMap = new HashMap<String, Object>(); // 通過屬性配置URL到Bean名的映射 public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } // 配置URL到Bean的映射 public void setUrlMap(Map<String, ?> urlMap) { this.urlMap.putAll(urlMap); } public Map<String, ?> getUrlMap() { return this.urlMap; } @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); // 初始化的時候註冊處理器 registerHandlers(this.urlMap); } protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { // 如果配置的處理器映射為空白,則警告 if (urlMap.isEmpty()) { logger.warn("Neither ‘urlMap‘ nor ‘mappings‘ set on SimpleUrlHandlerMapping"); } else { // 對於沒一個配置的URL到處理器的映射,如果URL不是以斜線(/)開頭,則追加斜線開頭,則註冊處理器 for (Map.Entry<String, Object> entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); } } } }
首先,它通過一個hashmap來儲存請求和controller的對應關係,也就是儲存註冊資訊。類都被Ioc容器管理者,HandlerMapping管理的是對應關係。其中key是http請求的path資訊,value可以是一個字串,或者是一個處理請求的HandlerExecutionChain,如果是String類型,則會將其視為Spring的bean名稱。在HandlerMapping對象的建立中,IoC容器執行了一個容器回調方法setApplicationContext,在這個方法中調用initApplicationContext方法進行初始化,各個子類可以根據需求的不同覆寫這個方法。關於handlerMap資訊的註冊就是在initApplicationContext方法中被執行的。
取得Handler的方法在他的父類中,源碼如下:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); //尋找符合匹配規則的handler。可能的結果是HandlerExecutionChain對象或者是null Object handler = lookupHandler(lookupPath, request); //如果沒有找到匹配的handler,則需要處理下default handler if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } //在getRootHandler和getDefaultHandler方法中,可能持有的是bean name。 if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = getApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } //如果handler還是為空白,則拋出錯誤。 if (handler != null && this.mappedInterceptors != null) { Set<HandlerInterceptor> mappedInterceptors = this.mappedInterceptors.getInterceptors(lookupPath, this.pathMatcher); if (!mappedInterceptors.isEmpty()) { HandlerExecutionChain chain; if (handler instanceof HandlerExecutionChain) { chain = (HandlerExecutionChain) handler; } else { chain = new HandlerExecutionChain(handler); } chain.addInterceptors(mappedInterceptors.toArray(new HandlerInterceptor[mappedInterceptors.size()])); } } if (handler != null && logger.isDebugEnabled()) { logger.debug("Mapping [" + lookupPath + "] to handler ‘" + handler + "‘"); } else if (handler == null && logger.isTraceEnabled()) { logger.trace("No handler mapping found for [" + lookupPath + "]"); } return handler; }
介面定義
看過了這些之後我們再回頭看看HandleraMapping介面的定義:
public interface HandlerMapping { String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping"; String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping"; String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; public abstract HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
眼尖的同學可能就要說了,這個介面下取得handler的方法怎麼傳回值是HandlerExecutionChain而不是一個handler呢?HandlerExecutionChain這個類,從名字可以直觀的看得出,這個對象是一個執行鏈的封裝。熟悉Struts2的都知道,Action對象也是被層層攔截器封裝,這裡可以做個類比,說明SpringMVC確實是吸收了Struts2的部分設計思想。大家可以再看看源碼,太長了我就不貼了。從源碼中可以看出,一個實質執行對象,還有一堆攔截器。這不就是Struts2中的實現麼,SpringMVC沒有避嫌,還是採用了這種封裝。得到HandlerExecutionChain這個執行鏈(execution chain)之後,下一步的處理將圍繞其展開。至此,HandlerExecutionChain整個執行脈絡也就清楚了:在真正調用其handler對象前,HandlerInterceptor介面實作類別組成的數組將會被遍曆,其preHandle方法會被依次調用,然後真正的handler對象將被調用。
小結:這裡主要的內容就是註冊和擷取的兩個過程,以及SpringMvc對handler的一些封裝。認真的讀讀源碼,就能很清晰的看到這個類的整個執行過程。加深對SpringMvc的執行過程的瞭解,感覺還是相當不錯的。
參考
http://blog.csdn.net/zhuojiajin/article/details/46292125
Spring MVC:handlermapping