SpringMVC源碼閱讀:核心分發器DispatcherServlet

1.前言

SpringMVC是目前J2EE平臺的主流Web框架,不熟悉的園友能夠看SpringMVC源碼閱讀入門,它交代了SpringMVC的基礎知識和源碼閱讀的技巧html

本文將介紹SpringMVC的核心分發器DispatcherServlet,經過源碼分析DispatcherServlet的運行過程web

2.DispatcherServlet的初始化

首先打開DispatcherServlet類繼承圖spring

能夠看到,DispatcherServlet繼承自HttpServlet,它的本質就是一個Servlet,這就是爲何上篇須要在web.xml經過url-mapping爲DispatcherServlet配置映射請求的緣由瀏覽器

咱們從HttpServletBean開始看,HttpServletBean重寫了其父類GenericServlet的init方法,咱們來看看init到底作了什麼(在啓動Tomcat的時候會進入init方法)mvc

ServletConfigPropertyValues是HttpServletBean的內部靜態類,它負責取到web.xml中contextConfigLocation,並addPropertyValue(),在PropertyValues能夠看到取到的值app

BeanWrapper是一個實體包裝類,簡單地說,BeanWrapper提供分析和操做JavaBean的方案,如值的set/get方法、描述的set/get方法以及屬性的可讀可寫性框架

ResourceLoader讀取到servletContext和classLoader,servletContext裝載了咱們剛纔的dispatcher-servlet.xml,classLoader找到咱們的字節碼文件並追蹤到咱們的jar包路徑,還有不少屬性不一一介紹,園友們能夠自行打斷點查看源碼分析

web.xml部分代碼,這就是咱們讀取的contextConfigLocationpost

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

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

 

回頭再看,這裏構造BeanWrapper,使用setPropertyValues設置PropertyValues,利用Spring依賴注入的特性初始化屬性,讀取web.xml的contextConfigLocation屬性用於構造Spring上下文url

用BeanWrapper的最大好處在於,咱們不須要再在HttpServletBean中定義contextConfigLocation屬性,並聲明調用set/get方法,BeanWrapper已經幫咱們作好了

 

按ctrl+alt+b,看initServletBean()到底在哪裏被實現

 

 接下來,咱們看FrameworkServlet這個類,該類繼承自HttpServletBean,看FrameworkServlet的initServletBean()方法

webApplicationContext是FrameworkServlet的上下文,initWebApplicationContext()方法爲當前Servlet初始化上下文

initFrameworkServlet()交由FrameworkServlet子類實現,默認實現爲空,該方法會在bean屬性和上下文加載後被調用

咱們如今看initWebApplicationContext()方法實現

獲取根上下文,並初始化一個空的上下文

527行不會進入if,只有上下文實例在構造的時候注入纔會調用

549行調用findWebApplicationContext()方法,這個方法用來查看該Servlet是否已經設置上下文,咱們點進去看,沒有獲得attrName,返回null

當FrameworkServlet沒有上下文實例定義時,調用createWebApplicationContext(),參數是咱們在initWebApplicationContext()中獲得的rootContext(根上下文),爲FrameworkServlet初始化上下文,設置id,environment,configLocation等屬性

560行onRefresh()是爲了防止構造注入上下文的時候沒有刷新,去手動刷新,在DispatcherServlet有實現

566行爲當前Servlet設置上下文

web.xml中配置的ContextLoaderListener根據applicationContext.xml生成上下文

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springConfig/dispatcher-servlet.xml</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

 進入ContextLoaderListener,打開其父類ContextLoader,常常用SpringMVC開發的人應該對ClassPathResource比較熟悉,ClassPathResource常常被咱們用來讀取資源文件

ContextLoader162行指向了ContextLoader.properties,一個配置文件,它指向了org.springframework.web.context.support.XmlWebApplicationContext這個類

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

在XmlWebApplicationContext loadBeanDefinitions()獲取到了contextConfigLocation,XmlBeanDefinitionReader使用xsd讀取xml文件,再也不詳述,園友能夠點進去看看

 

順着剛纔的思路,咱們看看DispatcherServlet,DispatcherServlet重寫了父類FrameworkServlet的onRefresh(ApplicationContext contex)方法

 

總結下HttpServletBean,FrameworkServlet和DispatcherServlet初始化過程

1.HttpServletBean

初始化web.xml中的參數

2.FrameworkServlet

將上下文賦予當前Servlet

3.DispatcherServlet

初始化HandlerMapping(請求映射),HandlerExceptionResolver(異常處理),ViewResolver(視圖解析)等功能實現類

 

 

3.DispatcherServlet處理請求

在瀏覽器輸入http://localhost:8080/springmvcdemo/employee,觸發DispatcherServlet的processRequest方法

我不得不說下其父類FrameworkServlet的processRequest方法

previousLocaleContext獲取和當前線程相關的LocaleContext

根據已有請求構造一個新的和當前線程相關的LocaleContext

previousAttributes獲取和當前線程綁定的RequestAttributes

爲已有請求構造新的ServletRequestAttributes,加入預綁定屬性

initContextHolders讓新構造的RequestAttributes和ServletRequestAttributes和當前線程綁定,加入到ThreadLocal,完成綁定

抽象方法doService由FrameworkServlet子類DispatcherServlet重寫

resetContextHolders方法解除RequestAttributes,ServletRequestAttributes和當前線程的綁定

註冊監聽事件ServletRequestHandledEvent,在調用上下文的時候產生Event

 

如今咱們看下DispatcherServlet的doService方法

attributesSnapshot用來保存request域中的數據,能夠叫作「快照」

進入doDispatch方法

 

接下來咱們看一看doDispatch方法,內容不少,我在這作些簡述,細節部分後續會逐一分析

932行checkMultipart方法將request轉化成Multipart request

936行HandlerExecutionChain獲取Handler,有攔截器、Bean、BeanFactory,並對應上請求的Controller和Service等等

943行HandlerAdapter獲取到各類argumentResolvers,用來解析參數,還能獲取到各類returnValueHandlers,用來處理類返回值(後續會詳解)

963行經過HandlerAdapter handle方法返回視圖模型ModelAndView

969行給ModelAndView設置viewName

970行使用applyPostHandle方法攔給已註冊的攔截器放行,咱們此時並無聲明攔截器,spring給咱們默認生成兩個默認已註冊的攔截器,以下

結束,文中不免有錯誤,但願園友能及時指出

3.參考

https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/htmlsingle/#mvc-servlet

相關文章
相關標籤/搜索