Spring MVC 基礎源碼學習(續)

在上一篇中主要介紹了spring mvc如何使用以及spring的DispatcherServlet加載細節以及URL映射配置,但是還是遺留了不少問題,現在就來具體討論下之前提出的問題以及有哪些解決方案。

  • rootContext 這是怎么一回事,是必須的么,和applicationContext.xml又有什么關系呢?
  • xml配置的context-param和servlet的init-param有什么區別?
  • cors跨域是什么,spring中如何使用
  • URL映射規則是如何完成的,以及和Tomcat的URL映射有什么關聯么?

1、rootContext

rootContext是wrapper的上一層也就是context創建的上下文,那就使得servlet和context存在各自的上下文,并且存在依賴關系。

如果現在在web.xml中配置了多個servlet,那就意味著各個servlet都必須設置自己的上下文,如果各個servlet都需要使用共同的bean,那么就可以設置一個他們的根上下文,也就是這里的rootContext,達到公用的目的。

使用方法

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
   <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/applicationContext.xml</param-value>
   </context-param>

這樣就可以為context創建一個上下文,他的bean信息通過WEB-INF/applicationContext.xml解析獲取,仔細看下ContextLoaderListener類就會發現他是繼承自ServletContextListener接口的。

Tomcat的源碼文件StandardContext類

image

spring的ContextLoader文件

image

這就很清楚ContextLoaderListener的調用關系了,而在使用本項目單步調試的時候,確實入口就是這里。

認真理解了根上下文以及dispatchservlet的上下文之后,才可以避免出現重復創建bean以及bean無法注入的各種情況。

2、context-param和init-param

context-param是作用在context的,也就是上面說的ContextLoaderListener的配置參數
init-param 是作用在servlet的,一般情況下是作為DispatcherServlet的配置參數

兩者分別設置好自身的上下文環境,不過需要注意到這兩種最后都是創建WebApplicationContext對象,如果沒有設置配置的參數,都會獲取默認的配置路徑的文件,都是/WEB-INF/applicationContext.xml,如果不存在該文件,spring在讀取xml文件會拋出資源不存在的錯誤。

在之前學習的時候,看到如果不設置配置文件,則一定要設置為applicationContext.xml,真正的原因其實也是這個默認屬性。

image

image

3、cors跨域是什么,spring mvc如何操作的。

跨域是瀏覽器為了確保安全的一種措施,當在A域名的頁面請求B域名的鏈接時就會因為跨域被瀏覽器攔截,從而無法傳送數據,可是實際開發中,跨域是非常頻繁的請求,除了等下我們介紹的spring cors方法外還有比如jsonp作為解決跨域問題的方案。

AbstractHandlerMapping 類

if (CorsUtils.isCorsRequest(request)) {
    CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
    // 獲取全局配置
    CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    // 獲取當前請求的配置
    CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
    // 獲得最終的可用的cors配置
    executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    // 重新生成可執行鏈
}

public static boolean isCorsRequest(HttpServletRequest request) {
   // 查看請求頭部是否存在Origin這個信息
    return (request.getHeader(HttpHeaders.ORIGIN) != null);
}   

更多的具體的可看 Spring MVC 與 CORS 講的蠻好的。

4、URL映射規則是如何完成的,以及和Tomcat的URL映射有什么關聯么?

首先需要明白一個情況就是,spring mvc在開發完之后打包成一個war包,由Tomcat啟動,那么入口其實是在Tomcat的上。

Tomcat的7.0版本(5.0?不是很確定了,4.0不是這樣情況)開始的URL映射都是通過mappingData關聯的,而mappingData的數據在一個request請求過來的就已經決定好的,可以知道具體選擇哪個wrapper,而上面已經說了,一個wrapper只可以關聯一個servlet,那就意味著到spring mvc這一層的時候就已經明確了由哪個servlet去繼續執行了。

image

在spring mvc中則是通過各個controller的包含了RequestMapping的方法得到具體的映射路徑,接下來具體了解下其過程,入口是initHandlerMappings(context)方法。

換做我們,我們會做怎樣做呢?一般情況就是掃描包路徑,所有的可能為controller的類,再掃描類中有RequestMapping注解的方法,合并對應的類信息,組成一個kv對,k是URL,v是某類一具體的方法。接下來具體看下spring是如何實現該操作的。

DispatchServlet 類

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;

        if (this.detectAllHandlerMappings) {
            // 需要全量查找,默認值是true
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            // 獲取IOC容器中所有為HandlerMapping的類
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
               // 如果不需要全量查找,則獲取handlerMapping類的bean信息
                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.
            }
        }
        
      // 確保必須得有一個URL的關聯映射  
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }
    
    
    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        String key = strategyInterface.getName();
        // key就是HandlerMapping的類名稱,org.springframework.web.servlet.HandlerMapping
        String value = defaultStrategies.getProperty(key);
        // value是在配置文件中的值,三個類名稱
        if (value != null) {
            String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
            List<T> strategies = new ArrayList<T>(classNames.length);
            for (String className : classNames) {
                try {
                    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                    // 得到具體的類信息
                    Object strategy = createDefaultStrategy(context, clazz);
                    // 按照創建一個bean的方式去創建一個類,并且獲取所有需要的信息
                    // 在這里會完成所有URL的映射記錄
                    strategies.add((T) strategy);
                }
                catch (ClassNotFoundException ex) {
                    throw new BeanInitializationException(
                            "Could not find DispatcherServlet's default strategy class [" + className +
                                    "] for interface [" + key + "]", ex);
                }
                catch (LinkageError err) {
                    throw new BeanInitializationException(
                            "Error loading DispatcherServlet's default strategy class [" + className +
                                    "] for interface [" + key + "]: problem with class file or dependent class", err);
                }
            }
            return strategies;
        }
        else {
            return new LinkedList<T>();
        }
    }
    

這個時候handlerMappings 已經存在了數據,只不過還只是映射到了controller,并沒有具體到方法


image

在上一節Spring MVC 源碼學習 已經介紹了接下來的URL是如何被映射上并處理的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,818評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,912評論 6 342
  • 要加“m”說明是MB,否則就是KB了. -Xms:初始值 -Xmx:最大值 -Xmn:最小值 java -Xms8...
    dadong0505閱讀 4,887評論 0 53
  • 這些屬性是否生效取決于對應的組件是否聲明為 Spring 應用程序上下文里的 Bean(基本是自動配置的),為一個...
    發光的魚閱讀 1,442評論 0 14
  • 青春須早為,豈能長少年。 ——孟郊 關于青春的美好時光值得我們用一生去回憶,總會有那樣一個人兒,始終是心底最不愿意...
    周皮皮bibby閱讀 3,267評論 65 40