SpringBoot源碼-mvc工作流程(上)

SpringMVC 這么重要因谎,怎么能錯(cuò)過(guò),搞起~

在初始化容器的時(shí)候肴敛,會(huì)把url與類方法的映射關(guān)系注冊(cè)進(jìn)去缰猴,一切從AbstractHandlerMethodMapping 類說(shuō)起,找到該類下的initHandlerMethods() 方法喊递,代碼如下:

protected void initHandlerMethods() {
    // 獲取容器初始化的bean,遍歷
    for (String beanName : getCandidateBeanNames()) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            processCandidateBean(beanName);
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

點(diǎn)擊進(jìn)入processCandidateBean()方法随闪,核心代碼如下:

protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    // 獲取bean的類型
    beanType = obtainApplicationContext().getType(beanName);
    // 如果有注解 @Controller 或 @RequestMapping,則進(jìn)入
    if (beanType != null && isHandler(beanType)) {
        detectHandlerMethods(beanName);
    }
}

isHandler()方法很簡(jiǎn)單骚勘,就是判斷beanType是否有 @Controller 或 @RequestMapping 注解:

protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType,equestMapping.class));
}

回到 processCandidateBean()方法铐伴,點(diǎn)擊 detectHandlerMethods()撮奏,進(jìn)入,核心代碼:

protected void detectHandlerMethods(Object handler) {
    Class<?> handlerType = (handler instanceof String ?
    obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 泛型T是實(shí)際是 RequestMappingInfo 類型
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
        (MethodIntrospector.MetadataLookup<T>) method -> {
            // 獲取方法的映射
            return getMappingForMethod(method, userType);
        });
        
        methods.forEach((method, mapping) -> {
            Method invocableMethod =                            
            AopUtils.selectInvocableMethod(method,userType);
            // 注冊(cè)方法
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

點(diǎn)擊 getMappingForMethod()方法当宴,核心代碼:

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 創(chuàng)建方法的 RequestMappingInfo
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // 創(chuàng)建類的 RequestMappingInfo
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // 合并方法和類的 @RequestMapping
            info = typeInfo.combine(info);
        }
        String prefix = getPathPrefix(handlerType);
        if (prefix != null) {
            info = RequestMappingInfo.paths(prefix).options(this.config)
                   .build().combine(info);
        }
    }
}

點(diǎn)擊 createRequestMappingInfo()進(jìn)去畜吊,代碼很簡(jiǎn)單:

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    // 找到 element 的 @RequestMapping 注解
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    RequestCondition<?> condition = (element instanceof Class ?
    getCustomTypeCondition((Class<?>) element) :   
    getCustomMethodCondition((Method) element));
    // 構(gòu)建 RequestMappingInfo 返回
    return (requestMapping != null ? 
            createRequestMappingInfo(requestMapping,condition) : null);
}

回到 getMappingForMethod()方法,點(diǎn)擊 typeInfo.combine(info) 進(jìn)去:

public RequestMappingInfo combine(RequestMappingInfo other) {
    String name = combineNames(other);
    PathPatternsRequestCondition pathPatterns =
    (this.pathPatternsCondition != null && other.pathPatternsCondition != null     ? this.pathPatternsCondition.combine(other.pathPatternsCondition) : null);

    PatternsRequestCondition patterns =
    (this.patternsCondition != null && other.patternsCondition != null ?
    this.patternsCondition.combine(other.patternsCondition) : null);

    RequestMethodsRequestCondition methods =               
                        this.methodsCondition.combine(other.methodsCondition);
    ParamsRequestCondition params =      
                        this.paramsCondition.combine(other.paramsCondition);
    HeadersRequestCondition headers =       
                        this.headersCondition.combine(other.headersCondition);
    ……

    return new RequestMappingInfo(name, pathPatterns, patterns,                
           methods, params, headers, consumes, produces, custom, this.options);
}

這個(gè)方法也很簡(jiǎn)單户矢,就是把 patterns玲献、methods、params梯浪、headers等合并起來(lái)捌年,構(gòu)建RequestMappingInfo 返回。

我們?cè)倩氐?detectHandlerMethods() 方法驱证,找到registerHandlerMethod()延窜,點(diǎn)擊進(jìn)入,核心代碼:

public void register(T mapping, Object handler, Method method) {
    // 構(gòu)建新的 handlerMethod
    HandlerMethod handlerMethod = createHandlerMethod(handler, method);
    Set<String> directPaths =       
                    AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
    for (String path : directPaths) {
        // path是 接口路徑抹锄,如 /a/b,mapping是 RequestMappingInfo
        this.pathLookup.add(path, mapping);
    }

    String name = null;
    if (getNamingStrategy() != null) {
        name = getNamingStrategy().getName(handlerMethod, mapping);
        // 把方法名和handlerMethod的映射添加到 nameLookup中
        addMappingName(name, handlerMethod);
    }

    this.registry.put(mapping, new MappingRegistration<>(
                mapping,handlerMethod, directPaths, name, corsConfig != null));
}

這里有兩個(gè)很重要的結(jié)構(gòu):

private final MultiValueMap<String, T> pathLookup = 
                                                new LinkedMultiValueMap<>();

private final Map<String, List<HandlerMethod>> nameLookup = 
                                                new ConcurrentHashMap<>();

這兩個(gè)變量存儲(chǔ)了url與類方法的關(guān)系荠藤。

看我的例子伙单,造的兩個(gè)接口:

兩個(gè)接口.png

pathLookup存儲(chǔ)的結(jié)構(gòu)信息:

pathLookup.png

nameLookup存儲(chǔ)的結(jié)構(gòu)信息:

nameLookup.png

各位細(xì)品,千言萬(wàn)語(yǔ)都匯聚在圖中~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哈肖,一起剝皮案震驚了整個(gè)濱河市吻育,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淤井,老刑警劉巖布疼,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異币狠,居然都是意外死亡游两,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門漩绵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贱案,“玉大人,你說(shuō)我怎么就攤上這事止吐”ψ伲” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵碍扔,是天一觀的道長(zhǎng)瘩燥。 經(jīng)常有香客問(wèn)我,道長(zhǎng)不同,這世上最難降的妖魔是什么厉膀? 我笑而不...
    開(kāi)封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上站蝠,老公的妹妹穿的比我還像新娘汰具。我一直安慰自己,他們只是感情好菱魔,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布留荔。 她就那樣靜靜地躺著,像睡著了一般澜倦。 火紅的嫁衣襯著肌膚如雪聚蝶。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天藻治,我揣著相機(jī)與錄音碘勉,去河邊找鬼。 笑死桩卵,一個(gè)胖子當(dāng)著我的面吹牛验靡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雏节,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胜嗓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了钩乍?” 一聲冷哼從身側(cè)響起辞州,我...
    開(kāi)封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寥粹,沒(méi)想到半個(gè)月后变过,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涝涤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年媚狰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妄痪。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哈雏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衫生,到底是詐尸還是另有隱情裳瘪,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布罪针,位于F島的核電站彭羹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏泪酱。R本人自食惡果不足惜派殷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一还最、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧毡惜,春花似錦拓轻、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至帕膜,卻和暖如春枣氧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垮刹。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工达吞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人荒典。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓酪劫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親寺董。 傳聞我的和親對(duì)象是個(gè)殘疾皇子契耿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容