Spring mvc源碼解析

一愿阐、Spring MVC中重要角色

1.DispatcherServlet:前端控制器垃喊,接受所有web.xml中配置的請(qǐng)求绎速,處理整個(gè)請(qǐng)求流程
2.HandlerMapping:處理映射器鉴裹,根據(jù)請(qǐng)求的URL赎线,找到對(duì)應(yīng)的Handler廷没,包括定義的攔截器
3.HandlerAdapter:Handler處理器適配器,處理不同類型的請(qǐng)求垂寥,主要包括有HttpReqeustHandlerAdapter颠黎、SimpleServletHandlerAdapter和SimpleControllerHandlerAdapter,這三種都比較簡(jiǎn)單滞项,都是通過(guò)方法直接調(diào)用狭归;RequestMappingHandlerAdapter這個(gè)處理器就是我們常用的注解形式,使用RequestMapping注解時(shí)的處理器文判,這種類型就是使用反射的方式調(diào)用过椎;
4.Controller:后端處理器,用來(lái)接受請(qǐng)求戏仓,處理后端業(yè)務(wù)邏輯
5.ViewResolver:視圖解析器疚宇,把邏輯視圖解析成真正的物理視圖
6.View:視圖,將數(shù)據(jù)展現(xiàn)給用戶

二赏殃、大致流程

我們先來(lái)看圖
Spring mvc執(zhí)行過(guò)程

1.用戶發(fā)起請(qǐng)求到前端控制器(DispatcherServlet)
2.前端控制器會(huì)找到處理映射器(HandlerMapping)根據(jù)請(qǐng)求的URL敷待,找到相應(yīng)的處理器執(zhí)行鏈
3.前端控制器找到處理器執(zhí)行鏈后,再找到處理器適配器仁热,找到處理器
4.執(zhí)行處理器
5.處理器會(huì)返回一個(gè)ModelAndView
6.前端控制器會(huì)請(qǐng)求視圖解析器解析視圖
7.視圖渲染榜揖,返回到用戶頁(yè)面

三、Spring MVC初始化流程源碼解析

之前的所有源碼分析,把整個(gè)過(guò)程的代碼都粘貼上來(lái)了举哟,感覺(jué)效率不高思劳,很多一些不重要的代碼占了大量的篇幅,導(dǎo)致整篇看下來(lái)抓不住重點(diǎn)妨猩;這次源碼只粘貼重要的代碼潜叛,這樣過(guò)程就會(huì)很清晰。
首先Spring MVC初始化册赛,是從我們?cè)賥eb.xml中配置DispatcherServlet钠导,開(kāi)始;DispatcherServlet繼承了FrameworkServlet森瘪,F(xiàn)rameworkServlet繼承了HttpServletBean,所以最開(kāi)始的初始化是從HttpServletBean中的init()方法開(kāi)始的

public final void init() throws ServletException {
    ...
    //我們主要看這個(gè)初始化的方法
    initServletBean();
    ...
}
protected final void initServletBean() throws ServletException {
    ...
    try {
        //初始WebApplicationContext容器
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    ...
}

protected WebApplicationContext initWebApplicationContext() {
    //獲取父容器票堵,如果我們配置了Spring的配置文件扼睬,會(huì)在這一步獲取到Spring的容器
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    ...
    if (wac == null) {
        //創(chuàng)建容器,把父容器傳進(jìn)去
        wac = createWebApplicationContext(rootContext);
    }
    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }
    if (this.publishContext) {
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                    "' as ServletContext attribute with name [" + attrName + "]");
        }
    }
    return wac;
}

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    //獲取到容器類型悴势,我們是配置在web.xml中的窗宇,使用的是XmlWebApplicationContext容器類型
    Class<?> contextClass = getContextClass();
    ...
    //實(shí)例化容器
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    wac.setEnvironment(getEnvironment());
    //設(shè)置父容器
    wac.setParent(parent);
    //獲取到我們的mvc配置文件
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    //根據(jù)配置初始化容器
    configureAndRefreshWebApplicationContext(wac);
    return wac;
}

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    ...
    wac.setServletContext(getServletContext());
    wac.setServletConfig(getServletConfig());
    wac.setNamespace(getNamespace());
    //主要是看這一步,這里注冊(cè)了一個(gè)容器初始化后的監(jiān)聽(tīng)器特纤,這一步是mvc初始化的關(guān)鍵
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }
    postProcessWebApplicationContext(wac);
    applyInitializers(wac)
    //初始化Spring容器军俊,這個(gè)我們?cè)賡pring源碼分析的時(shí)候,分析過(guò)spring的加載過(guò)程捧存,這里就不進(jìn)去看了
    wac.refresh();
}

這里就是容器初始化粪躬,我們主要是看到ContextRefreshListener這個(gè)監(jiān)聽(tīng)器的注冊(cè),接下來(lái)昔穴,我們看看ContextRefreshListener這個(gè)監(jiān)聽(tīng)器做了什么

/**
 *我們看到ContextRefreshListener這個(gè)監(jiān)聽(tīng)器镰官,監(jiān)聽(tīng)了ContextRefreshedEvent這個(gè)事件
 *也就是spring容器初始化完成的事件
 **/
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //執(zhí)行了FrameworkServlet的onApplicationEvent方法
        FrameworkServlet.this.onApplicationEvent(event);
    }
}
public void onApplicationEvent(ContextRefreshedEvent event) {
    this.refreshEventReceived = true;
    //執(zhí)行初始化mvc容器的方法
    onRefresh(event.getApplicationContext());
}
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
    //初始化上傳文件解析器
    initMultipartResolver(context);
    //初始化本地化解析器
    initLocaleResolver(context);
    //初始化主題解析器
    initThemeResolver(context);
    //初始化Handler映射器處理器組件
    initHandlerMappings(context);
    //初始化處理器適配器
    initHandlerAdapters(context);
    //初始化處理器異常解析器
    initHandlerExceptionResolvers(context);
    //初始化視圖解析器
    initRequestToViewNameTranslator(context);
    //初始化視圖組件
    initViewResolvers(context);
    //初始化分布管理者
    initFlashMapManager(context);
}

到這一步整個(gè)spring mvc初始化就完成了

四、spring mvc請(qǐng)求流程

因?yàn)镈ispatcherServlet繼承了FrameworkServlet吗货,F(xiàn)rameworkServlet繼承了HttpServletBean泳唠,HttpServletBean繼承了HttpServlet,也就是請(qǐng)求進(jìn)來(lái)時(shí)宙搬,會(huì)調(diào)用到doPost或者doGet方法去笨腥,F(xiàn)rameworkServlet覆蓋了這幾個(gè)方法,我們看下doGet方法

protected final void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    //無(wú)論是doPost還是doGet都是調(diào)用processRequest方法
    processRequest(request, response);
}

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    ...
    try {
        //執(zhí)行doService方法
        doService(request, response);
    }
    catch (ServletException | IOException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }
    finally {
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }
        if (logger.isDebugEnabled()) {
            if (failureCause != null) {
                this.logger.debug("Could not complete request", failureCause);
            }
            else {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    logger.debug("Leaving response open for concurrent processing");
                }
                else {
                    this.logger.debug("Successfully completed request");
                }
            }
        }
        //發(fā)布ServletRequestHandledEvent事件
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ...
    //設(shè)置request屬性勇垛,所以我們可以在controller中獲取到下面四個(gè)屬性
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
    
    ...
    try {
        //開(kāi)始進(jìn)入doDispatch脖母,這個(gè)就是前段控制器最重要的方法了,所有流程都在這個(gè)方法中
        doDispatch(request, response);
    }
    finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Restore the original attribute snapshot, in case of an include.
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }
}
//這個(gè)方法我們就不省略代碼了窥摄,這樣流程會(huì)清晰一點(diǎn)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    //handler執(zhí)行鏈镶奉,這個(gè)里會(huì)封裝所有攔截器的集合,還有執(zhí)行的handler
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            //從映射器中獲取處理器執(zhí)行鏈
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
            //從處理器適配器中獲取處理器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            //獲取請(qǐng)求方式
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (logger.isDebugEnabled()) {
                    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
            //執(zhí)行攔截器的preHandle方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            //使用處理器執(zhí)行相應(yīng)的handle,也就是我們的業(yè)務(wù)代碼
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
            //視圖處理(頁(yè)面渲染)
            applyDefaultViewName(processedRequest, mv);
            //執(zhí)行攔截器postHandle方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

Spring MVC的處理流程就結(jié)束了哨苛,太細(xì)節(jié)的東西就不講了鸽凶,大家自己有時(shí)間可以閱讀下源碼,例如怎么獲取處理器執(zhí)行鏈的建峭,怎么獲取處理器的玻侥,還有各種處理器是怎么去處理不同方式的請(qǐng)求的,帶著這些問(wèn)題去閱讀源碼亿蒸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凑兰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子边锁,更是在濱河造成了極大的恐慌姑食,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茅坛,死亡現(xiàn)場(chǎng)離奇詭異音半,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)贡蓖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)曹鸠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人斥铺,你說(shuō)我怎么就攤上這事彻桃。” “怎么了晾蜘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵邻眷,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我笙纤,道長(zhǎng)耗溜,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任省容,我火速辦了婚禮抖拴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腥椒。我一直安慰自己阿宅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布笼蛛。 她就那樣靜靜地躺著洒放,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滨砍。 梳的紋絲不亂的頭發(fā)上往湿,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天妖异,我揣著相機(jī)與錄音,去河邊找鬼领追。 笑死他膳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绒窑。 我是一名探鬼主播棕孙,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼些膨!你這毒婦竟也來(lái)了蟀俊?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤订雾,失蹤者是張志新(化名)和其女友劉穎肢预,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體葬燎,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡误甚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谱净。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡擅威,死狀恐怖壕探,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情郊丛,我是刑警寧澤李请,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站厉熟,受9級(jí)特大地震影響导盅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜揍瑟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一白翻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绢片,春花似錦滤馍、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至熙涤,卻和暖如春阁苞,著一層夾襖步出監(jiān)牢的瞬間困檩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工那槽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悼沿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓倦炒,卻偏偏與公主長(zhǎng)得像显沈,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逢唤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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