Spring MVC 學(xué)習(xí)筆記

1. Spring MVC核心類與接口

1.1 DispatcherServlet:前置控制器

Spring 提供的前置控制器,所有的請求都經(jīng)過它來統(tǒng)一分發(fā)。DispatcherServlet 將請求分發(fā)給 Spring Controller 之前,需要借助于 Spring 提供的 HandlerMapping 定位到具體的 Controller。
DispatcherServlet 也是間接最高繼承了 HttpServlet.

1.2 HandlerMapping 接口:處理請求的映射

HandlerMapping 接口的實(shí)現(xiàn)類:

  • SimpleUrlHandlerMapping通過配置文件,把一個(gè) URL 映射到 Controller 類上;
  • DefaultAnnotationHandlerMapping通過注解,例如 @RequestMapping,把一個(gè) URL 映射到 Controller 類上;

1.3 HandlerAdapter 接口:處理請求的映射

Spring MVC 通過 HandlerAdapter 來實(shí)際調(diào)用處理函數(shù)。

例如:
AnnotationMethodHandlerAdapter:DispatcherServlet 中根據(jù) HandlerMapping 找到對應(yīng)的 Handler Method 后,首先檢查當(dāng)前工程中注冊的所有可用的 HandlerAdapter,根據(jù) HandlerAdapter 中的 supports() 方法找到可以使用的 HandlerAdapter。
通過調(diào)用 HandlerAdapter 中的 handle() 方法來處理及準(zhǔn)備 Handler Method 中的參數(shù)及 annotation (這就是 Spring MVC 如何將 Reqeust中的參數(shù)變成 Handler Method 中的輸入?yún)?shù)的地方),最終調(diào)用實(shí)際的 Handler Method。
接口定義如下:

public interface HandlerAdapter {
    boolean supports(Object var1);

    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}

1.4 Controller接口:控制器

由于我們使用了 @Controller 注解,添加了 @Controller 注解的類就可以擔(dān)任控制器(Action)的職責(zé),所以我們并沒有用到這個(gè)接口。

需要為并發(fā)用戶處理請求,因此實(shí)現(xiàn) Controller 接口時(shí),必須保證線程安全并可重用。
一旦 Controller 處理完用戶請求,則返回 ModelAndView 對象給 DispatcherServlet 前置控制器,ModelAndView 中包含了模型(Model)和視圖(View)。

  • 從宏觀角度考慮,DispatcherServlet 是整個(gè) Web 應(yīng)用的控制器;
  • 從微觀考慮,Controller 是單個(gè) Http 請求處理過程中的控制器;
  • ModelAndView 是 HTTP 請求過程中返回的模型(Model)和視圖(View)。

1.5 HandlerInterceptor 接口:攔截器

1.6 ViewResolver 接口的實(shí)現(xiàn)類

Spring 提供的視圖解析器(ViewResolver)在 Web 應(yīng)用中查找 View 對象,從而將相應(yīng)結(jié)果渲染給客戶。
不同種類的 View 會對應(yīng)不同的 ViewResolver,例如:

  • JSP 需要用到 org.springframework.web.servlet.view.InternalResourceViewResolver
  • 模板引擎需要用到 org.springframework.web.servlet.view.tiles3.TilesViewResolver
  • 文件下載需要用到 org.springframework.web.servlet.view.BeanNameViewResolver

1.7 View 接口

View 也會有不同的實(shí)現(xiàn)類,例如返回 JSP 的 View 時(shí)需要用到 org.springframework.web.servlet.view.JstlView。

1.8 LocalResolver 接口

1.9 HandlerExceptionResolver 接口:異常處理

1.10 ModelAndView 類

2. Spring 啟動過程

對于一個(gè) Web 應(yīng)用,其部署在 Web 容器中(例如 Tomcat),Web 容器提供其一個(gè)全局的上下文環(huán)境,這個(gè)上下文就是 ServletContext其為后面的 Spring IoC 容器提供宿主環(huán)境。

在應(yīng)用 web.xml 中會提供有 ContextLoaderListener,例如:

<!--監(jiān)聽器-->
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

在 Web 容器(例如 Tomcat)啟動時(shí),會觸發(fā)容器初始化事件,此時(shí) ContextLoaderListener 會監(jiān)聽到這個(gè)事件,其 contextInitialized 方法會被調(diào)用,在這個(gè)方法中,Spring 會初始化一個(gè)啟動上下文,這個(gè)上下文被稱為根上下文,即 WebApplicationContext,這是一個(gè)接口類,確切的說,其實(shí)際的實(shí)現(xiàn)類是 XmlWebApplicationContext。這個(gè)就是 Spring的 IoC 容器,其對應(yīng)的 Bean 定義的配置由 web.xml 中的 context-param 標(biāo)簽指定。

public void contextInitialized(ServletContextEvent event) {
    this.initWebApplicationContext(event.getServletContext());
}

ContextLoaderListener 監(jiān)聽器初始化完畢后,開始初始化 web.xml 中配置的 Servlet,這個(gè) Servlet 可以配置多個(gè),以最常見的DispatcherServlet為例,這個(gè) Servlet 實(shí)際上是一個(gè)標(biāo)準(zhǔn)的前端控制器,用以轉(zhuǎn)發(fā)、匹配、處理每個(gè) Servlet 請求。

DispatcherServlet 上下文在初始化的時(shí)候會建立自己的 IoC 上下文,用以持有 Spring MVC 相關(guān)的 Bean。在建立 DispatcherServlet 自己的 IoC 上下文時(shí),會先從 ServletContext 中獲取之前的根上下文(WebApplicationContext)作為自己上下文的 parent 上下文。有了這個(gè) parent 上下文之后,再初始化自己持有的上下文。

當(dāng) Web 項(xiàng)目啟動時(shí),做初始化工作,所以我們大部分是配置在 web.xml 里面:

<web-app>

  <display-name>Web Application</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-*.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>springMVC_rest</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>
  </servlet>

  <servlet-mapping>
    <servlet-name>springMVC_rest</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

圖片引用自 http://www.itdecent.cn/p/dc64d02e49ac

Web應(yīng)用部署初始化流程圖

圖片引用自 https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc

image

3. DispatcherServlet 初始化過程

DispatcherServlet 繼承了 FrameworkServlet,FrameworkServlet 繼承了 HttpServletBean,HttpServletBean 繼承了 HttpServlet 類。

第一步:HttpServletBean 類init() 方法

HttpServletBean 類有一個(gè)入口點(diǎn)就是重寫了 init 方法。

  • 先通過 PropertyValues 獲取 web.xml 文件 init-param 的參數(shù)值;
  • 然后通過 ResourceLoader 讀取 .xml 配置信息;
  • BeanWrapper 對配置的標(biāo)簽進(jìn)行解析和將系統(tǒng)默認(rèn)的 bean 的各種屬性設(shè)置到對應(yīng)的 bean 屬性;

第二步:FrameworkServlet 類 initServletBean() 方法

initWebApplicationContext() 初始化上下文,并作為值放到了 ServletContext 里,因?yàn)椴煌?DispatherServlet 有對應(yīng)的各自的上下文,而且上下文有設(shè)置父上下文和 id 屬性等。
上下文項(xiàng)目啟動時(shí)會調(diào)用 createWebApplicationContext() 方法。

第三步:DispatcherServlet 類 onRefresh() 方法

DispatcherServlet 初始化各個(gè)功能的實(shí)現(xiàn)類。比如異常處理、視圖處理、請求映射處理等。

// 初始化上傳文件解析器
initMultipartResolver(context);
// 初始化本地解析器
initLocaleResolver(context);
// 初始化主題解析器
initThemeResolver(context);
// 初始化映射處理器
initHandlerMappings(context);
// 初始化適配器處理器
initHandlerAdapters(context);
// 初始化異常處理器
initHandlerExceptionResolvers(context);
// 初始化請求到視圖名翻譯器
initRequestToViewNameTranslator(context);
// 初始化視圖解析器
initViewResolvers(context);

4. DispatcherServlet 處理請求過程

圖片引用自 https://terasolunaorg.github.io/guideline/5.0.0.RELEASE/en/Overview/SpringMVCOverview.html

image

  • 用戶向服務(wù)器發(fā)送請求,請求被 Spring 前置控制 Servelt DispatcherServlet 捕獲;
  • DispatcherServlet 對請求 URL 進(jìn)行解析,得到請求資源標(biāo)識符(URI)。然后根據(jù)該 URI,調(diào)用 HandlerMapping 獲得該 Handler 配置的所有相關(guān)的對象(包括 Handler 對象以及 Handler 對象對應(yīng)的攔截器),最后以 HandlerExecutionChain 對象的形式返回;
  • DispatcherServlet 根據(jù)請求獲得 Handler,選擇一個(gè)合適的 HandlerAdapter。(如果成功獲得 HandlerAdapter 后,此時(shí)將開始執(zhí)行攔截器的 preHandler(...) 方法)
  • 提取 Request 中的模型數(shù)據(jù),填充 Handler 入?yún)ⅲ_始執(zhí)行 Handler(Controller)。 在填充 Handler 的入?yún)⑦^程中,根據(jù)你的配置,Spring 將幫你做一些額外的工作:
    • HttpMessageConveter:將請求消息(如 JSON、XML 等數(shù)據(jù))轉(zhuǎn)換成一個(gè)對象,將對象轉(zhuǎn)換為指定的響應(yīng)信息;
    • 數(shù)據(jù)轉(zhuǎn)換:對請求消息進(jìn)行數(shù)據(jù)轉(zhuǎn)換。如 String 轉(zhuǎn)換成 Integer、Double 等;
    • 數(shù)據(jù)根式化:對請求消息進(jìn)行數(shù)據(jù)格式化。 如將字符串轉(zhuǎn)換成格式化數(shù)字或格式化日期等;
    • 數(shù)據(jù)驗(yàn)證: 驗(yàn)證數(shù)據(jù)的有效性(長度、格式等),驗(yàn)證結(jié)果存儲到 BindingResultError 中;
  • Handler 執(zhí)行完成后,向 DispatcherServlet 返回一個(gè) ModelAndView 對象;
  • 根據(jù)返回的 ModelAndView,選擇一個(gè)適合的 ViewResolver 返回給 DispatcherServlet;
  • ViewResolver 結(jié)合 ModelView,來渲染視圖;
  • 將渲染結(jié)果返回給客戶端;

引用:
WEB請求處理五:MVC框架請求處理
spring 啟動過程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 學(xué)習(xí)資料源:慕課網(wǎng) - Spring MVC起步 內(nèi)容概要 一、前端控制器(Front Controller) 二...
    拾壹北閱讀 2,084評論 0 22
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,647評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,281評論 6 342
  • 1、Spring MVC請求流程 (1)初始化:(對DispatcherServlet和ContextLoderL...
    拾壹北閱讀 2,019評論 0 12
  • 領(lǐng)略過電梯驚魂后,我在項(xiàng)目選擇上更為謹(jǐn)慎,畢竟兒子還小,太刺激的項(xiàng)目是不合適的。所以,所有瘋狂的過山車都不考慮了!...
    陽光小同閱讀 330評論 0 0

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