The DispatcherServlet
Spring的Web MVC框架與許多其他Web MVC框架一樣,是請求驅動的,圍繞一個中央Servlet(即DispatcherServlet)設計,該Servlet將請求分派給控制器,并提供其他功能以促進Web應用程序的開發。然而,Spring的DispatcherServlet不僅僅是請求分派器,它與Spring的IoC容器完全集成,因此可以使用Spring的所有其他特性。
請求處理工作流
Spring Web MVC的DispatcherServlet的請求處理工作流如下圖所示:
- 客戶端發送請求:用戶通過瀏覽器發送一個HTTP請求。
- DispatcherServlet接收請求:請求到達中央的DispatcherServlet,它是整個請求處理的核心。
- HandlerMapping:DispatcherServlet使用HandlerMapping確定請求應該發送到哪個處理器(Controller)。
- HandlerAdapter:找到合適的Handler后,DispatcherServlet使用HandlerAdapter來執行處理器。
- 處理請求:處理器(Controller)處理請求,執行相應的業務邏輯,并返回一個ModelAndView對象,包含模型數據和視圖名稱。
- 視圖解析:DispatcherServlet使用ViewResolver將邏輯視圖名解析為實際的視圖對象(例如JSP頁面)。
- 生成響應:視圖對象負責渲染頁面,將模型數據填充到視圖中,并生成最終的HTTP響應返回給客戶端。
The request processing workflow in Spring Web MVC (high level)
DispatcherServlet的設計模式
熟悉設計模式的讀者會認識到,DispatcherServlet是"前端控制器"(Front Controller)設計模式的一種表達方式。這個模式在許多領先的Web框架中都很常見。
前端控制器(Front Controller)設計模式
前端控制器設計模式的核心思想是使用一個中央控制器來處理所有的請求,然后根據請求的不同,將其分派給適當的處理器。這種模式的優勢在于:
- 集中控制:所有請求通過一個入口點進行處理,方便進行統一的安全檢查、日志記錄和異常處理。
- 模塊化設計:請求處理邏輯被分散到不同的控制器中,使得代碼更加模塊化和可維護。
- 靈活性:可以根據不同的請求類型或URL模式靈活地選擇合適的處理器進行處理。
配置DispatcherServlet
在Java EE Servlet 3.0+環境中配置Spring的DispatcherServlet,可以通過標準的Java EE Servlet配置來實現。DispatcherServlet是一個實際的Servlet(它繼承自HttpServlet基類),因此需要在Web應用程序中聲明,并通過URL映射來處理特定的請求。
1. 使用代碼配置DispatcherServlet
首先,實現WebApplicationInitializer
接口,以便在Servlet容器啟動時進行配置。
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.servlet.DispatcherServlet;public class MyWebApplicationInitializer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext container) throws ServletException {// 創建并注冊DispatcherServletServletRegistration.Dynamic registration = container.addServlet("example", new DispatcherServlet());registration.setLoadOnStartup(1);registration.addMapping("/example/*");}
}
在上述代碼中,所有以/example
開頭的請求將由名為example
的DispatcherServlet實例處理。
2. 使用AbstractAnnotationConfigDispatcherServletInitializer
配置DispatcherServlet
這是Spring MVC推薦的設置方式,簡化了DispatcherServlet的注冊過程,只需指定其Servlet映射和配置類。
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[] { RootConfig.class };}@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[] { WebConfig.class };}@Overrideprotected String[] getServletMappings() {return new String[] { "/example/*" };}
}
3. 使用web.xml
配置DispatcherServlet
如果更喜歡使用傳統的web.xml
文件,可以如下配置DispatcherServlet:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd"version="3.0"><servlet><servlet-name>example</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>example</servlet-name><url-pattern>/example/*</url-pattern></servlet-mapping>
</web-app>
ApplicationContext和WebApplicationContext
在Spring Web MVC框架中,每個DispatcherServlet都有其自己的WebApplicationContext
,它繼承了根WebApplicationContext
中已經定義的所有bean。
- 根WebApplicationContext:包含所有需要在整個應用程序中共享的基礎設施bean,如數據源、事務管理器等。
- WebApplicationContext:特定于每個DispatcherServlet,可以在此范圍內覆蓋根上下文中的bean,并定義新的特定于Servlet實例的bean。
例如:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RootConfig {@Beanpublic DataSource dataSource() {// 配置數據源}
}@Configuration
@EnableWebMvc
@ComponentScan("com.example.myapp")
public class WebConfig {@Beanpublic ViewResolver viewResolver() {// 配置視圖解析器}
}
在上述配置中,RootConfig
類中的bean將在根WebApplicationContext
中共享,而WebConfig
類中的bean僅在特定的WebApplicationContext
中可用。
總結
通過以上方法,可以在Servlet 3.0+環境中配置Spring的DispatcherServlet。推薦使用代碼配置方式,尤其是通過AbstractAnnotationConfigDispatcherServletInitializer
,因為它更加簡潔和靈活。同時,理解根WebApplicationContext
與每個DispatcherServlet的WebApplicationContext
之間的關系,有助于更好地組織和管理Spring應用程序中的bean。
Typical context hierarchy in Spring Web MVC(Spring Web MVC的典型上下文層次結構)
在初始化 DispatcherServlet 時,Spring MVC 會在您的 Web 應用程序的 WEB-INF 目錄中尋找一個名為 [servlet-name]-servlet.xml 的文件,并創建其中定義的 bean。如果某個 bean 在全局范圍內已經定義過了,那么在這個 servlet 特定的上下文中的定義會覆蓋全局范圍內的定義。這種機制允許每個 DispatcherServlet 都可以擁有自己獨立的配置和 bean 定義,以滿足特定于 DispatcherServlet 的需求。
請考慮以下 Servlet 配置(在文件中):DispatcherServlet``web.xml
<web-app><servlet><servlet-name>golfing</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>golfing</servlet-name><url-pattern>/golfing/*</url-pattern></servlet-mapping>
</web-app>
根據上述Servlet配置,需要在應用程序中擁有一個名為 /WEB-INF/golfing-servlet.xml
的文件。這個文件將包含所有Spring Web MVC特定的組件(bean),例如控制器、視圖解析器等等。
對于單個DispatcherServlet場景,也可以只有一個根上下文。根上下文中的bean可以被多個DispatcherServlet實例共享使用,這在應用程序需要多個DispatcherServlet處理不同URL模式時很有用。
Single root context in Spring Web MVC
這可以通過設置一個空的 contextConfigLocation servlet init 參數來配置, 如下所示:
<web-app><context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/root-context.xml</param-value></context-param><servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value></param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/*</url-pattern></servlet-mapping><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>
</web-app>
配置解釋
- 全局上下文 (Root Context):
<context-param>
元素配置了全局上下文的配置文件路徑:/WEB-INF/root-context.xml
。這是通過ContextLoaderListener
加載的,它創建了一個全局的 Spring 應用上下文(根上下文),這個上下文在整個 Web 應用程序中共享。
- DispatcherServlet 專用上下文 (Dispatcher Context):
<servlet>
元素配置了DispatcherServlet
,它通常會有自己的專用 Spring 應用上下文,通常被稱為子上下文。<init-param>
中的contextConfigLocation
參數為空(即:<param-value></param-value>
),這表示DispatcherServlet
將不使用單獨的配置文件,而是繼承全局上下文(根上下文)的配置。
具體作用
- 當
contextConfigLocation
參數為空時,DispatcherServlet
會默認查找名為[servlet-name]-servlet.xml
的配置文件。對于此配置,它會查找dispatcher-servlet.xml
。 - 如果該文件不存在,
DispatcherServlet
將不使用任何特定于它的配置文件,而是僅依賴于全局上下文(根上下文)。
這種配置的好處
- 簡化配置
- 可以避免為
DispatcherServlet
創建額外的配置文件,簡化配置管理。
- 可以避免為
- 共享全局配置
DispatcherServlet
直接使用根上下文的配置,使得全局 bean 定義可以被DispatcherServlet
使用,減少重復配置。
WebApplicationContext 是 ApplicationContext 的擴展,專門為 Web 應用程序提供額外的功能。它與普通的 ApplicationContext 不同之處在于:
-
主題解析: WebApplicationContext 能夠解析和管理主題(themes),主題是定義 Web 應用程序外觀和感覺的靜態資源集合,如 CSS 和圖片等。
-
與 Servlet 的關聯: WebApplicationContext 能夠知道它所關聯的 Servlet。這通過與 ServletContext 的鏈接實現,ServletContext 提供了對 Web 應用程序資源和配置的訪問。
-
綁定在 ServletContext 中: WebApplicationContext 被綁定在 ServletContext 中,因此可以在整個 Web 應用程序的生命周期中訪問它。
通過 RequestContextUtils 類的靜態方法,你可以隨時查找 WebApplicationContext,以便在需要時訪問它。
請注意,我們可以通過基于 Java 的配置實現相同的目的:
public class GolfingWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {// GolfingAppConfig defines beans that would be in root-context.xmlreturn new Class<?>[] { GolfingAppConfig.class };}@Overrideprotected Class<?>[] getServletConfigClasses() {// GolfingWebConfig defines beans that would be in golfing-servlet.xmlreturn new Class<?>[] { GolfingWebConfig.class };}@Overrideprotected String[] getServletMappings() {return new String[] { "/golfing/*" };}
}
Special Bean Types In the WebApplicationContext
在Spring MVC中,DispatcherServlet 使用特殊的 bean 來處理請求并渲染適當的視圖。這些特殊的 bean 是 Spring MVC 的一部分。你可以通過在 WebApplicationContext 中配置其中一個或多個來選擇使用哪些特殊的 bean。但是,最初你不需要這樣做,因為如果沒有配置任何特殊的 bean,Spring MVC 會使用默認的一組 bean。下面是列出的一些特殊 bean 類型及其功能:
Special bean types in the WebApplicationContext
Bean type | Explanation |
---|---|
HandlerMapping | Maps incoming requests to handlers and a list of pre- and post-processors (handler interceptors) based on some criteria the details of which vary by HandlerMapping implementation. The most popular implementation supports annotated controllers but other implementations exists as well. |
HandlerAdapter | Helps the DispatcherServlet to invoke a handler mapped to a request regardless of the handler is actually invoked. For example, invoking an annotated controller requires resolving various annotations. Thus the main purpose of a HandlerAdapter is to shield the DispatcherServlet from such details. |
HandlerExceptionResolver | Maps exceptions to views also allowing for more complex exception handling code. |
ViewResolver | Resolves logical String-based view names to actual View types. |
LocaleResolver & LocaleContextResolver | Resolves the locale a client is using and possibly their time zone, in order to be able to offer internationalized views |
ThemeResolver | Resolves themes your web application can use, for example, to offer personalized layouts |
MultipartResolver | Parses multi-part requests for example to support processing file uploads from HTML forms. |
FlashMapManager | Stores and retrieves the “input” and the “output” FlashMap that can be used to pass attributes from one request to another, usually across a redirect. |
這里是中文形式的表格:
Bean 類型 | 描述 |
---|---|
HandlerMapping | 確定請求如何映射到具體的處理器(控制器)。 |
HandlerAdapter | 調用選定的處理器來處理請求。 |
HandlerExceptionResolver | 解析處理器(控制器)拋出的異常,并提供適當的處理方式(例如錯誤頁面或 JSON 響應)。 |
ViewResolver | 將邏輯視圖名稱解析為實際的視圖對象(如 JSP、Thymeleaf、Freemarker 等),并處理視圖的渲染。 |
LocaleResolver | 解析客戶端請求的區域信息,使應用程序能夠提供語言和文化特定的內容。 |
ThemeResolver | 解析客戶端請求的主題信息,允許動態切換和管理不同的視覺主題。 |
Bean 類型 | 默認實現類 |
------------------------ | ------------------------------------------------------------ |
HandlerMapping | BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping |
HandlerAdapter | HttpRequestHandlerAdapter 和 RequestMappingHandlerAdapter |
HandlerExceptionResolver | DefaultHandlerExceptionResolver |
ViewResolver | InternalResourceViewResolver |
LocaleResolver | AcceptHeaderLocaleResolver |
ThemeResolver | FixedThemeResolver |
詳細說明
-
HandlerMapping(處理器映射):
BeanNameUrlHandlerMapping
:將 URL 請求映射到具有特定名稱的 bean。RequestMappingHandlerMapping
:基于注解的請求映射,處理使用@RequestMapping
注解的控制器方法。
-
HandlerAdapter(處理器適配器):
HttpRequestHandlerAdapter
:處理實現了HttpRequestHandler
接口的處理器。RequestMappingHandlerAdapter
:處理基于注解的控制器,適配@RequestMapping
注解的方法。
-
HandlerExceptionResolver(處理器異常解析器):
DefaultHandlerExceptionResolver
:默認的異常解析器,處理常見的異常類型,如NoSuchRequestHandlingMethodException
、HttpRequestMethodNotSupportedException
等。
-
ViewResolver(視圖解析器):
InternalResourceViewResolver
:將邏輯視圖名稱解析為內部資源視圖(如 JSP 文件),通過轉發請求來渲染視圖。- 示例:
-
LocaleResolver(區域解析器):
AcceptHeaderLocaleResolver
:根據請求頭中的Accept-Language
信息解析區域設置。
-
ThemeResolver(主題解析器):
FixedThemeResolver
:使用固定的主題,不支持動態切換主題。
這些默認實現類提供了基礎的功能,可以滿足大多數 Web 應用程序的需求。如果有特定的需求,可以通過在配置文件中定義自定義的 bean 來覆蓋這些默認實現。
Default DispatcherServlet Configuration
-
默認配置文件 DispatcherServlet.properties:
- DispatcherServlet 在
org.springframework.web.servlet
包下的DispatcherServlet.properties
文件中維護了特殊 bean 的默認實現列表。每種特殊 bean 都有其默認的實現。
- DispatcherServlet 在
-
定制特殊 bean:
- 雖然每種特殊 bean 都有合理的默認設置,但遲早你會需要定制其中一個或多個 bean 的屬性。例如,常見的做法是配置
InternalResourceViewResolver
的prefix
屬性,指定視圖文件的父路徑。
- 雖然每種特殊 bean 都有合理的默認設置,但遲早你會需要定制其中一個或多個 bean 的屬性。例如,常見的做法是配置
-
覆蓋默認實現:
- 當你在 WebApplicationContext 中配置特殊 bean(例如
InternalResourceViewResolver
)時,實際上是覆蓋了原本會被用作該特殊 bean 類型的默認實現列表。例如,如果配置了InternalResourceViewResolver
,那么默認的ViewResolver
實現列表就會被忽略。
- 當你在 WebApplicationContext 中配置特殊 bean(例如
-
其他配置選項:
- 在第 22.16 節 “Configuring Spring MVC” 中,你將了解到配置 Spring MVC 的其他選項,包括 MVC Java 配置和 MVC XML 命名空間。這些選項為配置 Spring MVC 提供了簡單的起點,假設你對 Spring MVC 的工作原理了解不多。不論你如何選擇配置你的應用程序,本節中解釋的概念都是基礎且有益的。
總結來說,這段話強調了在配置 Spring MVC 應用程序時,理解和定制 DispatcherServlet 的特殊 bean 是至關重要的。通過配置特殊 bean,你可以根據應用程序的需求修改默認的行為,并控制請求處理和視圖解析的細節。
DispatcherServlet Processing Sequence
當設置好 DispatcherServlet 并且接收到該 DispatcherServlet 的請求時,DispatcherServlet 開始按照以下步驟處理請求:
-
綁定 WebApplicationContext:
- DispatcherServlet 首先在請求中查找并綁定 WebApplicationContext。這個 WebApplicationContext 默認以
DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE
作為鍵名綁定在請求中,控制器和其他處理流程中的元素可以使用它。WebApplicationContext 包含了整個應用程序的配置和 bean 定義。
- DispatcherServlet 首先在請求中查找并綁定 WebApplicationContext。這個 WebApplicationContext 默認以
-
綁定 LocaleResolver:
- LocaleResolver 負責解析請求的區域設置信息,以便在處理請求時確定要使用的語言和文化。如果應用程序不需要國際化支持,可以忽略這一步驟。
-
綁定 ThemeResolver:
- ThemeResolver 負責解析請求的主題信息,允許視圖根據請求動態切換和管理不同的視覺主題。如果應用程序不使用主題功能,可以忽略這一步驟。
-
處理文件上傳(如果需要):
- 如果配置了 multipart 文件解析器,DispatcherServlet 將檢查請求是否包含多部分內容(文件上傳)。如果有多部分內容,請求將被包裝在 MultipartHttpServletRequest 中,以便其他處理流程進一步處理。關于多部分處理的更多信息,請參見第 22.10 節 “Spring 的多部分(文件上傳)支持”。
-
查找適當的處理器(Handler):
- DispatcherServlet 查找與請求匹配的處理器。如果找到處理器,將按照處理器關聯的執行鏈(預處理器、后處理器和控制器)的順序執行,以準備模型數據或進行渲染。
-
渲染視圖或處理異常:
- 如果處理器返回了模型數據,則進行視圖渲染。如果處理過程中拋出了異常,那么聲明在 WebApplicationContext 中的處理器異常解析器將捕獲這些異常,并根據配置的方式處理異常情況,例如返回特定的錯誤頁面或 JSON 響應。
-
支持最后修改日期(Last-Modified):
- DispatcherServlet 還支持根據 Servlet API 指定的最后修改日期返回給客戶端。它通過查找適當的處理器映射,并檢查找到的處理器是否實現了 LastModified 接口來確定特定請求的最后修改日期。
-
自定義 DispatcherServlet:
- 你可以通過向 web.xml 文件中的 Servlet 聲明添加 Servlet 初始化參數(init-param 元素)來定制單個 DispatcherServlet 實例。下表列出了支持的參數列表。
這些步驟和功能說明了 DispatcherServlet 在處理請求時的詳細流程,以及如何通過配置和定制來適應應用程序的特定需求。
DispatcherServlet initialization parameters
Parameter | Explanation |
---|---|
contextClass | Class that implements ConfigurableWebApplicationContext , to be instantiated and locally configured by this Servlet. By default, XmlWebApplicationContext is used. |
contextConfigLocation | String that is passed to the context instance (specified by contextClass ) to indicate where context(s) can be found. The string consists potentially of multiple strings (using a comma as a delimiter) to support multiple contexts. In case of multiple context locations with beans that are defined twice, the latest location takes precedence. |
namespace | Namespace of the WebApplicationContext . Defaults to [servlet-name]-servlet . |
關于 DispatcherServlet 可配置參數的表格及其解釋:
參數 | 解釋 |
---|---|
contextClass | 實現了 ConfigurableWebApplicationContext 接口的類,將由此 Servlet 實例化并進行本地配置。默認情況下,使用 XmlWebApplicationContext 。這個參數允許你指定用于創建 WebApplicationContext 的具體類。 |
contextConfigLocation | 傳遞給上述 contextClass 實例的字符串,指示可以找到上下文(或多個上下文)的位置。這個字符串可以是多個位置,使用逗號作為分隔符。在多個上下文位置的情況下,如果有重復定義的 bean,則以最后一個位置為準。這個參數允許你指定上下文配置文件的位置。 |
namespace | WebApplicationContext 的命名空間。默認為 [servlet-name]-servlet 。這個參數影響 Servlet 創建的 ApplicationContext 的命名空間,用于在 Servlet 容器中唯一標識 ApplicationContext。 |
這些參數允許你在 web.xml
文件中通過 init-param
元素為單個 DispatcherServlet 實例進行定制配置。通過配置這些參數,你可以控制 DispatcherServlet 如何實例化和配置其關聯的 WebApplicationContext,以滿足特定應用程序的需求。