SpringMVC初始化之Controller&RequestMapping

前言

是Java開發(fā)的小伙伴就一定會使用SpringMVC,沒有SpringBoot的年代或許我們還需要配置一些xml文件疆导。但是到了SpringBoot時代,Java程序員只需要使用@Controller悠菜、@RequestMapping等注解就OK了败富,一切都變得非常簡單兽叮,你只需要專注業(yè)務(wù)代碼就可以。但是專注業(yè)務(wù)代碼的同時账阻,我們還是需要了解這背后的@Controller泽本,@RequestMapping背后的故事。

SpringMVC啟動涉及解析Controller&RequestMapping的步驟

  1. 獲取Spring工廠中所有的Bean蒲牧。
  2. 在所有的Bean中找出被@Controller@RequestMapping注解的類,比如說找到A類显熏。
  3. 獲取A類滿足以下條件的方法:public方法晒屎、被@RequestMapping注解標記的方法鼓鲁,比如找到方法b。(父類的方法也會被找出來)橙弱。
  4. 獲取方法bRequestMapping信息燥狰,獲取A類上的RequestMapping(如果有的話)信息,將兩者信息進行合并新的RequestMapping蛀缝。(比如組合路徑目代,類: /a, 方法:/b ->組合 /a/b)
  5. 方法bA類的beanName(或者A類對象)構(gòu)建HandlerMethod在讶,以合并的新RequestMapping信息為key霜大,以HandlerMethodvalue存入內(nèi)存中。

數(shù)據(jù)存儲如下

{"requestMapping合并信息":"handlerMethod"}
image.png

介紹完基本步驟后,接下來就是在源碼中把關(guān)鍵的代碼找出來就可以了湖笨。

入口

這里先介紹下SpringMVC加載這些步驟的入口-RequestMappingHandlerMapping慈省,這個類負責加載這些步驟,所以大家可以打開RequestMappingHandlerMapping這個類一起跟看源碼袱衷。

詳解

入口方法是在RequestMappingHandlerMapping父類AbstractHandlerMethodMapping中笑窜,在文件中找到這個下面方法

protected void initHandlerMethods() {
                  // 這是第一步:獲取所有beanName
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));

        for (String beanName : beanNames) {
               // 這個是用來判斷bean是不是由ScopedProxyUtils這個工具類生成的排截,可以不用管,全當這個判斷不存在
    
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class<?> beanType = null;
                try {
                    beanType = getApplicationContext().getType(beanName);
                }
                catch (Throwable ex) {
    
                    }
                }
                               // 這是第二步:找到只有包含Controller 和 RequestMapping的bean 
                if (beanType != null && isHandler(beanType)) {
                              //剩下步驟在這里處理
                    detectHandlerMethods(beanName);
                }
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

剩下步驟具體代碼

protected void detectHandlerMethods(final Object handler) {
                 // 這個handler 就是被Controller或RequestMappering 標記的bean
        Class<?> handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
                // 第四步:找出符合條件的方法脱吱,并合并方法和類的RequestMapping信息箱蝠。
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                new MethodIntrospector.MetadataLookup<T>() {
                    @Override
                    public T inspect(Method method) {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                        
                        }
                    }
                });

        for (Map.Entry<Method, T> entry : methods.entrySet()) {
            Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
            T mapping = entry.getValue();
                        // 第五步:放入內(nèi)存
            registerHandlerMethod(handler, invocableMethod, mapping);
        }
    }

第四步驟:合并方法和類的RequestMapping信息

        
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 獲取方法中的RequestMapping的信息
         RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
                        // 獲取類的RequestMapping信息
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                                // 合并RequestMapping信息
                info = typeInfo.combine(info);
            }
        }
        return info;
    }

第五步驟:合并的信息存入內(nèi)存

public void register(T mapping, Object handler, Method method) {
            this.readWriteLock.writeLock().lock();
            try {
                                // HandlerMethod是不是有點熟悉宦搬,就是SpringMVC攔截器中的那個參數(shù)劫拗。
                               //HandlerMethod 包含bean對象以及對應(yīng)的方法
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                assertUniqueMethodMapping(handlerMethod, mapping);
                                // ReqeustMapping合并信息放入內(nèi)存
                this.mappingLookup.put(mapping, handlerMethod);
                                
                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                            //將相對路徑與RequestMapping對應(yīng)起來;  /aa/dd
                    this.urlLookup.add(url, mapping);
                }

                String name = null;
                if (getNamingStrategy() != null)
                             
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    addMappingName(name, handlerMethod);
                }

                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

下面攔截器中的handler參數(shù)就是HandlerMethod

image.png

其實在用戶請求的時候撇簿,SpringMVC會根據(jù)用戶請求的相對路徑在urlLookup中找出RquestMapping信息四瘫,然后根據(jù)RequestMapping找出HandlerMethod欲逃,所以關(guān)系就對應(yīng)起來了。關(guān)系如下圖洗做。

image.png

SpringMVC的攔截器中的預(yù)處理和后處理就是在反射前后執(zhí)行彰居。關(guān)系就如下圖所示陈惰。


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市井辆,隨后出現(xiàn)的幾起案子杯缺,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異千诬,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門馏艾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琅摩,“玉大人锭硼,你說我怎么就攤上這事『湟欤” “怎么了暑始?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵廊镜,是天一觀的道長。 經(jīng)常有香客問我嗤朴,道長,這世上最難降的妖魔是什么股缸? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任吱雏,我火速辦了婚禮,結(jié)果婚禮上替劈,老公的妹妹穿的比我還像新娘得滤。我一直安慰自己懂更,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布龄捡。 她就那樣靜靜地躺著慷暂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奸腺。 梳的紋絲不亂的頭發(fā)上血久,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天突照,我揣著相機與錄音,去河邊找鬼氧吐。 笑死讹蘑,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的筑舅。 我是一名探鬼主播座慰,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼豁翎!你這毒婦竟也來了角骤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤心剥,失蹤者是張志新(化名)和其女友劉穎邦尊,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝉揍,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年畦娄,在試婚紗的時候發(fā)現(xiàn)自己被綠了又沾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弊仪。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖杖刷,靈堂內(nèi)的尸體忽然破棺而出励饵,到底是詐尸還是另有隱情,我是刑警寧澤滑燃,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布役听,位于F島的核電站,受9級特大地震影響表窘,放射性物質(zhì)發(fā)生泄漏典予。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一乐严、第九天 我趴在偏房一處隱蔽的房頂上張望瘤袖。 院中可真熱鬧,春花似錦昂验、人聲如沸捂敌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽黍匾。三九已至,卻和暖如春呛梆,著一層夾襖步出監(jiān)牢的瞬間锐涯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工填物, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纹腌,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓滞磺,卻偏偏與公主長得像升薯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子击困,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354