SpringMVC運(yùn)行機(jī)制詳解——學(xué)好基本功,走路好輕松

說明:了解SpringMVC運(yùn)行機(jī)制,有利于加深對(duì)SpringMVC框架的理解、在開發(fā)過程中能夠快速定位到問題所在。同時(shí)對(duì)搭建SSM項(xiàng)目基礎(chǔ)配置更加理解??傊?,好處多多;

SpringMVC基本概念

SpringMVC框架:屬于SpringFrameWork的后續(xù)產(chǎn)品,已經(jīng)融合在Spring Web Flow里面(來自百度百科)??偠灾?,SpringMVC框架屬于Spring架構(gòu)中的組成部分。在實(shí)際開發(fā)中,它主要負(fù)責(zé)HTTP請(qǐng)求接收、數(shù)據(jù)處理、請(qǐng)求響應(yīng)等web層功能實(shí)現(xiàn);故SpringMVC也稱為Web層框架;它的設(shè)計(jì)架構(gòu)是基于MVC架構(gòu)。

MVC架構(gòu)概念

M(Model)模型、V(View)視圖、C(Controller)控制器三層分離的軟件設(shè)計(jì)思想;主要目的就讓M模型層和V視圖層進(jìn)行分離,只專注實(shí)現(xiàn)自己的功能。它們之間的交互的通過C控制器層進(jìn)行協(xié)調(diào)交。MVC分層的設(shè)計(jì)思想降低了代碼之間的耦合,有利于代碼維護(hù)。MVC架構(gòu)圖如下所示:


image

耦合度:是指業(yè)務(wù)代碼之間相互依賴、不同功能代碼分層不明確。雜糅在一起,類似于的線團(tuán),如下圖所示:左圖就是耦合度高的、有圖就是耦合度低的;

SpringMVC基本結(jié)構(gòu)

SpringMVC好比一輛車,由很多部件一起結(jié)合組成SpringMVC框架。組成SpringMVC部件主要有DispatcherServlet(分發(fā)器)、HandlerMapping(處理映射器)、HandlerAdapter(處理適配器)、ViewResolver(視圖解析器)等主要部件組成。

  1. DispatcherServlet(分發(fā)器):它屬于SpringMVC核心,好比汽車的發(fā)動(dòng)機(jī)的重要性。主要用來接收請(qǐng)求、相應(yīng)結(jié)果、調(diào)度其他組件的處理/響應(yīng)請(qǐng)求。由于它的存在,降低了各個(gè)組件之間的耦合;
  2. HandlerMapping(處理映射器):根據(jù)請(qǐng)求的URL去匹配程序中以“配置/注解”的方式配置的Handler,然后返回Handler處理器鏈。
  3. HandlerAdapter(處理適配器):根據(jù)規(guī)則去執(zhí)行相應(yīng)的Handler處理器,返回的是處理器處理后的結(jié)果;
  4. ViewResolver(視圖解析器):將適配器返回的結(jié)果ModelAndView,解析成相應(yīng)的View視圖對(duì)象,最后對(duì)View進(jìn)行渲染將處理結(jié)果通過頁面展示給用戶。

總結(jié):以上主要組件都是圍繞著DispatcherServlet(分發(fā)器)進(jìn)行調(diào)度的。它們之間不能直接交互。SpringMVC流程圖如下所示:

在這里插入圖片描述

SpringMVC源碼分析(看不懂記住上面就夠了,等知識(shí)儲(chǔ)備多了再學(xué)習(xí))

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

建議用Idea查看代碼,方便的不要不要的。用ctrl+鼠標(biāo)單擊DispatcherServlet進(jìn)入源碼文件;先來看DispatcherServlet源碼繼承/實(shí)現(xiàn)關(guān)系圖。如下圖所示:


在這里插入圖片描述

說明:以下根據(jù)源碼分析DispatcherServlet運(yùn)行機(jī)制流程,不會(huì)很具體講解各個(gè)方法的作用;

第一問:DispatcherServlet怎樣得到用戶的HTTP請(qǐng)求?

從上圖可知,DispatcherServlet繼承了HttpServlet,用過struts的都知道HtppServlet的作用。它是用處理HTTP請(qǐng)求的。具體自行百度。HttpServlet.class源碼方法匯總?cè)缦拢?/p>

在這里插入圖片描述

注意紅線標(biāo)記的地方。類DispatcherServlet繼承了抽象類FrameworkServlet。并且在抽象類FrameworkServlet中覆蓋了HttpServlet中的service方法。覆蓋方法源碼如下:

/**
*FrameworkServlet.class源碼(行數(shù):432——440)
**/
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
            super.service(request, response);
        } else {
            this.processRequest(request, response);
        }

    }

這說明了當(dāng)HTTP請(qǐng)求過來時(shí),會(huì)執(zhí)行以上方法,如果HTTP請(qǐng)求正常,則調(diào)用HttpServlet類中service方法。判斷是否請(qǐng)求類型,然后回調(diào)FrameworkServlet抽象類中的doGet/doPost方法。最終執(zhí)行processRequest方法。processRequest源碼如下圖所示:


在這里插入圖片描述

注意圖標(biāo)記的地方;doService是抽象類FrameworkServlet中的抽象方法。并且在子類DispatcherServlet中實(shí)現(xiàn)了。

總結(jié):到這為止,知道了DispatcherServlet是通過繼承抽象類FrameworkServlet,抽象類FrameworkServlet重寫了HttpServlet類的service方法得到HTTP請(qǐng)求;

第二問:HandlerMapping、HandlerAdapte和ViewResolver是怎樣初始化?
先看HttpServletBean.class類中init方法源碼。如下所示:

/**
* HttpServletBean.class中66-85行
**/
public final void init() throws ServletException {
        PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                this.initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            } catch (BeansException var4) {
                if (this.logger.isErrorEnabled()) {
                    this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
                }

                throw var4;
            }
        }

        this.initServletBean();
    }

類HttpServletBean中的init()重寫了類servlet中的init方法。init 方法的執(zhí)行時(shí)刻跟servlet 配置中的load-on-startup標(biāo)簽有關(guān),如下圖示所示:


在這里插入圖片描述

如果值大于等于 0,則在 Servlet 實(shí)例化的時(shí)候執(zhí)行,間隔時(shí)間由具體的值決定,值越大,則越遲執(zhí)行。如果小于 0 或者沒有配置,則在第一次請(qǐng)求的時(shí)候才同步執(zhí)行 (該方法只執(zhí)行一次);
其中init方法中涉及到了BeanWrapper,PropertyValues,ResourceLoader,initServletBean方法

  1. PropertyValues:獲取Web.xml里面的servlet的init-param;
  2. BeanWrapper:封裝了bean的行為,提供了設(shè)置和獲取屬性值,它有對(duì)應(yīng)的BeanWrapperImpl;
  3. ResourceLoader:接口僅有一個(gè)getResource(String location)的方法,可以根據(jù)一個(gè)資源地址加載文件資源。classpath:這種方式指定SpringMVC框架bean配置文件的來源;
  4. initServletBean():在HttpServletBean類只是提供了參考,具體實(shí)現(xiàn)實(shí)在子類FrameworkServlet中。源碼如下:


    在這里插入圖片描述

initServletBean方法調(diào)用了initWebApplicationContext方法。用來初始化springMVC上下文環(huán)境。同時(shí)通過synchronized關(guān)鍵同步執(zhí)行onRefresh()方法。源碼如下:


在這里插入圖片描述

onRefresh方法在FrameworkServlet只是提供參考,具體實(shí)現(xiàn)實(shí)在子類DispatcherServlet中。源碼如下:


在這里插入圖片描述

onRefresh方法中調(diào)用了initStrategies方法。在initStrategies初始化了SpringMVC的組件。
//初始化上傳文件解析器
initMultipartResolver(context);

//初始化本地解析器
initLocaleResolver(context);

//初始化主題解析器
initThemeResolver(context);

//初始化映射處理器
initHandlerMappings(context);

//初始化適配器處理器
initHandlerAdapters(context);

//初始化異常處理器
initHandlerExceptionResolvers(context);

//初始化請(qǐng)求到視圖名翻譯器
initRequestToViewNameTranslator(context);

//初始化視圖解析器
initViewResolvers(context);

總結(jié):目前分析的源碼中只涉及了3個(gè)類,分別是:FrameworkServlet、DispatcherServlet、HttpServletBean。它們各自做了什么事情?

  1. HttpServletBean是主要是獲取web.xml配置文件中的<init-param>標(biāo)簽的值;
  2. FrameworkServlet主要實(shí)現(xiàn)了初始化SpringMVC上下文環(huán)境;
  3. DispatcherServlet主要實(shí)現(xiàn)了SpringMVC各個(gè)組件的初始化;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容