摘要:由于抽象類重寫了父類的方法,所以此時會調用的方法,在該方法中通過調用父類的方法,該方法通過模板方法模式最終調到類的方法。分析該類間接實現了接口,直接實現該接口的是抽象類,映射與請求。
概述
在前一章https://segmentfault.com/a/1190000014901736的基礎上繼續分析,主要完成SimpleUrlHandlerMapping類的原理。
本系列文章是基于Spring5.0.5RELEASE。
類圖在分析類之前,先了解下類的繼承關系,如下圖:
紅框的類就是我們本章要分析的類。
創建/初始化從類圖關系上可以看出,SimpleUrlHanderMapping類最終實現了ApplicationContextAware接口,該接口定義了方法setApplicationContext(applicationContext),其作用是實現該接口的類,在Spring實例化類時,自動調用setApplicationContext(applicationContext)方法。
ApplicationObjectSupport抽象類實現了ApplicationContextAware接口的setApplicationContext(applicationContext)方法。
由于WebApplicationObjectSupport抽象類重寫了父類ApplicationObjectSupport的initApplicationContext(context)方法,所以此時會調用WebApplicationObjectSupport的initAppliationContext(context)方法,在該方法中通過suppr調用父類的initApplicationContext(context)方法,該方法通過模板方法模式最終調到SimpleUrlHandlerMapping類的initApplicationContext()方法。
整個創建涉及的類比較多,過程比較復雜,文字描述也很乏味,所以我畫了調用時序圖,可供大家參考:
分析到此,我們就找到了SimpleUrlHandlerMapping類的入口方法,即本類的initApplicationContext()方法。
分析SimpleUrlHandlerMapping
該類間接實現了org.springframework.web.servlet.HandlerMapping接口,直接實現該接口的是org.springframework.web.servlet.handler.AbstractHandlerMapping抽象類,映射Url與請求handler bean。支持映射bean實例和映射bean名稱。源代碼如下:
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { // 存儲url和bean映射 private final MapurlMap = new LinkedHashMap<>(); // 注入property的name為mappings映射 public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } // 注入property的name為urlMap映射 public void setUrlMap(Map urlMap) { this.urlMap.putAll(urlMap); } public Map getUrlMap() { return this.urlMap; } // 實例化本類實例入口 @Override public void initApplicationContext() throws BeansException { // 調用父類AbstractHandlerMapping的initApplicationContext方法,只要完成攔截器的注冊 super.initApplicationContext(); // 處理url和bean name,具體注冊調用父類完成 registerHandlers(this.urlMap); } // 注冊映射關系,及將property中的值解析到map對象中,key為url,value為bean id或name protected void registerHandlers(Map urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither "urlMap" nor "mappings" set on SimpleUrlHandlerMapping"); } else { urlMap.forEach((url, handler) -> { // 增加以"/"開頭 if (!url.startsWith("/")) { url = "/" + url; } // 去除handler bean名稱的空格 if (handler instanceof String) { handler = ((String) handler).trim(); } // 調用父類AbstractUrlHandlerMapping完成映射 registerHandler(url, handler); }); } } }
從以上代碼可知,SimpleUrlHandlerMapping類主要接收用戶設定的url與handler的映射關系,其實際的工作都是交由其父類來完成的。
AbstractHandlerMapping
在創建初始化SimpleUrlHandlerMapping類時,調用其父類的initApplicationContext()方法,該方法完成攔截器的初始化,代碼如下:
@Override protected void initApplicationContext() throws BeansException { // 空實現。 // 子類可重寫此方法以注冊額外的攔截器 extendInterceptors(this.interceptors); // 從上下文中查詢攔截器并添加到攔截器列表中 detectMappedInterceptors(this.adaptedInterceptors); // 初始化攔截器 initInterceptors(); } // 查找實現了MappedInterceptor接口的bean,并添加到映射攔截器列表 protected void detectMappedInterceptors(ListmappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( obtainApplicationContext(), MappedInterceptor.class, true, false).values()); } // 將自定義bean設置到適配攔截器中,bean需實現HandlerInterceptor或WebRequestInterceptor protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } }
AbstractUrlHandlerMapping
在創建初始化SimpleUrlHandlerMapping類時,調用AbstractUrlHandlerMapping類的registerHandler(urlPath,handler)方法,該方法源碼如下:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // 不是懶加載,默認為false,即不是,通過配置SimpleUrlHandlerMapping屬性lazyInitHandlers的值進行控制 // 如果不是懶加載并且handler為單例,即從上下文中查詢實例處理,此時resolvedHandler為handler實例對象; // 如果是懶加載或者handler不是單例,即resolvedHandler為handler邏輯名 if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); // 如果handler是單例,通過bean的scope控制 if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { // 把url與handler(名稱或實例)放入map,以供后續使用 this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
到此,SimpleUrlHandlerMapping類在容器啟動期間的初始化完成。
總結本文分析了SimpleUrlHandlerMapping類初始化過程,其實核心就是把url和handler進行了映射,供后續訪問使用,單靠看文章無法掌握。整個過程調用很復雜,大家多debug跟蹤,一定能了解其內部的邏輯。大家共勉!
最后創建了qq群方便大家交流,可掃描加入,同時也可加我qq:276420284,共同學習、共同進步,謝謝!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69466.html
摘要:接口接口作用是將請求映射到處理程序,以及預處理和處理后的攔截器列表,映射是基于一些標準的,其中的細節因不同的實現而不相同。該參數是類型,作用是檢查所有的映射解析器或使用或為的,默認為,即從上下文中檢查所有的。 概述 在Spring MVC啟動章節https://segmentfault.com/a/1190000014674239,介紹到了DispatcherServlet的onRef...
摘要:概述通過前三章的分析,我們簡要分析了和,但對攔截器部分做詳細的分析,攔截器的加載和初始化是三個相同的部分。 概述 通過前三章的分析,我們簡要分析了SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping和RequestMappingHandlerMapping,但對攔截器部分做詳細的分析,攔截器的加載和初始化是三個HandlerMapping相...
摘要:與類圖對比,類繼承自抽象類,其又繼承自抽象類,再往上繼承關系與一致。創建初始化上一章我們分析了的創建初始化過程,的創建初始化過程與一樣,方法的入口在抽象類中的方法。至此,代碼編寫完畢。 概述 本節我們繼續分析HandlerMapping另一個實現類BeanNameUrlHandlerMapping,從類的名字可知,該類會根據請求的url與spring容器中定義的bean的name屬性值...
摘要:概述上一節我們分析了的初始化過程,即創建并注冊,本章我們分析下的請求處理過程,即查找。本系列文章是基于。最后創建了群方便大家交流,可掃描加入,同時也可加我,共同學習共同進步,謝謝 概述 上一節我們分析了RequestMappingHandlerMapping的初始化過程,即創建并注冊HandlerMehtod,本章我們分析下RequestMappingHandlerMapping的請求...
摘要:默認支持該策略。以上是對的宏觀分析,下面我們進行內部細節分析。整體流程一通過實現接口,完成攔截器相關組件的初始化調用類的方法。總結本文主要分析了的初始化過程,希望對大家有幫助。隨著學習的深入,后面有時間在分析下期中涉及的關鍵,比如等等。 概述 本節我們繼續分析HandlerMapping另一個實現類ReqeustMappingHandlerMapping,該類是我們日常開發中使用最多的...
閱讀 2372·2021-11-15 11:37
閱讀 2631·2021-09-23 11:21
閱讀 2957·2021-09-07 10:11
閱讀 3170·2019-08-30 15:53
閱讀 2829·2019-08-29 15:13
閱讀 1612·2019-08-26 13:57
閱讀 1104·2019-08-26 12:23
閱讀 2445·2019-08-26 11:51