18.2 分发

Spring的Web MVC框架与许多其他Web MVC框架一样,以请求为驱动,围绕一个中央Servlet设计,将请求发送给控制器,并提供了其他促进Web应用程序开发的功能。然而, Spring 的DispatcherServlet 做得更多.它和 Spring IoC 容器整合一起,它允许你使用Spring 每个特性.

Spring Web MVC DispatcherServlet的请求处理工作流程如下图所示。 对设计模式熟悉的读者将会认识到,DispatcherServlet是“前端控制器”设计模式的表达(这是Spring Web MVC与许多其他领先的Web框架共享的模式)。

下图,在Spring Web MVC 中请求处理流程

18.2 分发 - 图1

DispatcherServlet是一个实际的Servlet(它继承自HttpServlet基类),因此在Web应用程序中被声明。 您需要使用URL映射来映射要DispatcherServlet处理的请求。 以下是Servlet 3.0+环境中的标准Java EE Servlet配置:

  1. public class MyWebApplicationInitializer implements WebApplicationInitializer {
  2. @Override
  3. public void onStartup(ServletContext container) {
  4. ServletRegistration.Dynamic registration = container.addServlet("example", new DispatcherServlet());
  5. registration.setLoadOnStartup(1);
  6. registration.addMapping("/example/*");
  7. }
  8. }

在前面的示例中,以/ example开头的所有请求都将由名为Example的DispatcherServlet实例处理。

WebApplicationInitializer是由Spring MVC提供的接口,可确保您的基于代码的配置被检测并自动用于初始化任何Servlet 3容器。这个名为AbstractAnnotationConfigDispatcherServletInitializer的接口的抽象基类实现通过简单地指定其servlet映射和列出配置类来更容易地注册DispatcherServlet,甚至建议您设置Spring MVC应用程序。有关更多详细信息,请参阅基于代码的Servlet容器初始化。

DispatcherServlet是一个实际的Servlet(它继承自HttpServlet基类),因此在Web应用程序的web.xml中声明。您需要通过使用同一web.xml文件中的URL映射来映射要DispatcherServlet处理的请求。这是标准的Java EE Servlet配置;以下示例显示了这样的DispatcherServlet声明和映射:

  1. <web-app>
  2. <servlet>
  3. <servlet-name>example</servlet-name>
  4. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  5. <load-on-startup>1</load-on-startup>
  6. </servlet>
  7. <servlet-mapping>
  8. <servlet-name>example</servlet-name>
  9. <url-pattern>/example/*</url-pattern>
  10. </servlet-mapping>
  11. </web-app>

如第3.15节“ApplicationContext的附加功能”中所述,Spring中的ApplicationContext实例可以被限定。 在Web MVC框架中,每个DispatcherServlet都有自己的WebApplicationContext,它继承了已经在根WebApplicationContext中定义的所有bean。 根WebApplicationContext应该包含应该在其他上下文和Servlet实例之间共享的所有基础架构bean。 这些继承的bean可以在特定于servlet的范围内被覆盖,您可以在给定的Servlet实例本地定义新的范围特定的bean。

18.2. Spring Web MVC中的典型上下文层次结构

18.2 分发 - 图2

在初始化DispatcherServlet时,Spring MVC将在Web应用程序的WEB-INF目录中查找名为[servlet-name] -servlet.xml的文件,并创建在那里定义的bean,覆盖使用相同名称定义的任何bean的定义 在全球范围内。 请考虑以下DispatcherServlet Servlet配置(在web.xml文件中):

  1. <web-app>
  2. <servlet>
  3. <servlet-name>golfing</servlet-name>
  4. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  5. <load-on-startup>1</load-on-startup>
  6. </servlet>
  7. <servlet-mapping>
  8. <servlet-name>golfing</servlet-name>
  9. <url-pattern>/golfing/*</url-pattern>
  10. </servlet-mapping>
  11. </web-app>

使用上述Servlet配置,您将需要在应用程序中有一个名为/WEB-INF/golfing-servlet.xml的文件; 该文件将包含您所有的Spring Web MVC特定组件(bean)。 您可以通过Servlet初始化参数更改此配置文件的确切位置(有关详细信息,请参阅下文)。 单个DispatcherServlet方案也可能只有一个根上下文。

18.2 分发 - 图3

这可以通过设置一个空的ContextConfigLocation servlet init参数进行配置,如下所示:

  1. <web-app>
  2. <context-param>
  3. <param-name>contextConfigLocation</param-name>
  4. <param-value>/WEB-INF/root-context.xml</param-value>
  5. </context-param>
  6. <servlet>
  7. <servlet-name>dispatcher</servlet-name>
  8. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  9. <init-param>
  10. <param-name>contextConfigLocation</param-name>
  11. <param-value></param-value>
  12. </init-param>
  13. <load-on-startup>1</load-on-startup>
  14. </servlet>
  15. <servlet-mapping>
  16. <servlet-name>dispatcher</servlet-name>
  17. <url-pattern>/*</url-pattern>
  18. </servlet-mapping>
  19. <listener>
  20. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  21. </listener>
  22. </web-app>

WebApplicationContext是普通ApplicationContext的扩展,它具有Web应用程序所需的一些额外功能。 它与正常的ApplicationContext不同之处在于它能够解析主题(参见第18.9节“使用主题”),并且知道它与哪个Servlet相关联(通过连接到ServletContext)。 WebApplicationContext绑定在ServletContext中,并且通过在RequestContextUtils类上使用静态方法,您可以随时查找WebApplicationContext,如果您需要访问它。 请注意,我们可以通过基于Java的配置实现相同的方式:

  1. public class GolfingWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
  2. @Override
  3. protected Class<?>[] getRootConfigClasses() {
  4. // GolfingAppConfig defines beans that would be in root-context.xml
  5. return new Class[] { GolfingAppConfig.class };
  6. }
  7. @Override
  8. protected Class<?>[] getServletConfigClasses() {
  9. // GolfingWebConfig defines beans that would be in golfing-servlet.xml
  10. return new Class[] { GolfingWebConfig.class };
  11. }
  12. @Override
  13. protected String[] getServletMappings() {
  14. return new String[] { "/golfing/*" };
  15. }
  16. }

18.2.1 WebApplicationContext中的特殊Bean类型

Spring DispatcherServlet使用特殊的bean来处理请求并呈现适当的视图。 这些bean是Spring MVC的一部分。 您可以通过在WebApplicationContext中简单配置一个或多个选择要使用的特殊bean。 但是,您最初不需要这样做,因为Spring MVC维护一个默认bean列表,如果您没有配置任何内容。 更多的在下一节。 首先看下表列出DispatcherServlet依赖的特殊bean类型。

表18.1. 在 WebApplicationContext中的特殊bean类型

Bean type Explanation
HandlerMapping 根据一些标准将传入的请求映射到处理程序和前处理程序和后处理程序列表(处理程序拦截器),其细节由HandlerMapping实现而异。 最流行的实现支持注释控制器,但其他实现也存在。
HandlerAdapter 帮助DispatcherServlet调用映射到请求的处理程序,而不管实际调用哪个处理程序。 例如,调用带注释的控制器需要解析各种注释。 因此,HandlerAdapter的主要目的是屏蔽DispatcherServlet和这些细节
HandlerExceptionResolver 映射视图的异常,也允许更复杂的异常处理代码。
ViewResolver 将基于逻辑字符串的视图名称解析为实际的View类型。
LocaleResolver&LocaleContextResolver 解决客户端正在使用的区域设置以及可能的时区,以便能够提供国际化的视图
ThemeResolver 解决您的Web应用程序可以使用的主题,例如,提供个性化的布局
MultipartResolver 解析multi-part请求,以支持从HTML表单处理文件上传。
FlashMapManager 存储并检索可以用于将属性从一个请求传递到另一个请求的“输入”和“输出”FlashMap,通常是通过重定向。

18.2.2 默认DispatcherServlet 配置

如上一节中针对每个特殊bean所述,DispatcherServlet会维护默认使用的实现列表。此信息保存在包org.springframework.web.servlet中的文件DispatcherServlet.properties中。

所有特殊豆都有一些合理的默认值。不久之后,您将需要自定义这些bean提供的一个或多个属性。例如,将InternalResourceViewResolver设置的前缀属性配置为视图文件的父位置是很常见的。

无论细节如何,在这里了解的重要概念是,一旦您在WebApplicationContext中配置了一个特殊的bean(如InternalResourceViewResolver),您可以有效地覆盖该特殊bean类型所使用的默认实现列表。例如,如果配置了InternalResourceViewResolver,则会忽略ViewResolver实现的默认列表。

在第18.16节“配置Spring MVC”中,您将了解配置Spring MVC的其他选项,包括MVC Java配置和MVC XML命名空间,这两者都提供了一个简单的起点,并且对Spring MVC的工作原理几乎不了解。无论您如何选择配置应用程序,本节中介绍的概念都是基础的,应该对您有所帮助。

18.2.3 DispatcherServlet 处理序列

在您设置了DispatcherServlet并且针对该特定DispatcherServlet启动了一个请求后,DispatcherServlet将按如下所示开始处理请求:

  • 在请求中搜索并绑定WebApplicationContext作为控件和进程中的其他元素可以使用的属性。默认情况下,它将在DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE键下绑定。
  • 语言环境解析器被绑定到请求,以使进程中的元素能够解决在处理请求时使用的区域设置(渲染视图,准备数据等)。如果您不需要语言环境解析,则不需要它。
  • 主题解析器被绑定到使得诸如视图之类的元素确定要使用哪个主题的请求。如果不使用主题,可以忽略它。
  • 如果指定了多部分文件解析器,则会检查该请求的多部分;如果找到多部分,则请求被包装在一个MultipartHttpServletRequest中,以便进程中的其他元素进一步处理。有关多部分处理的更多信息,请参见第18.10节“Spring的多部分(文件上传)支持”。搜索适当的处理程序。如果找到处理程序,则执行与处理程序(预处理程序,后处理程序和控制器)关联的执行链,以便准备模型或呈现。

  • 如果返回模型,则呈现视图。如果没有返回模型(可能是由于预处理程序或后处理程序拦截请求,可能是出于安全原因),因为请求可能已经被满足,所以不会呈现任何视图。

  • 在WebApplicationContext中声明的处理程序异常解析程序在处理请求期间提取异常。使用这些异常解析器允许您定义自定义行为来解决异常。

Spring DispatcherServlet还支持返回由Servlet API指定的最后修改日期。确定特定请求的最后修改日期的过程很简单:DispatcherServlet查找适当的处理程序映射,并测试发现的处理程序是否实现了LastModified接口。如果是,则LastModified接口的long getLastModified(request)方法的值将返回给客户端。

您可以通过将Servlet初始化参数(init-param元素)添加到web.xml文件中的Servlet声明来自定义单独的DispatcherServlet实例。有关支持的参数列表,请参见下表。

表18.2. DispatcherServlet 初始化参数

参数 解释
contextClass 实现WebApplicationContext的类,它实例化了这个Servlet使用的上下文。 默认情况下,使用XmlWebApplicationContext。
contextConfigLocation 传递给上下文实例(由contextClass指定)以指示可以找到上下文的字符串。 该字符串可能包含多个字符串(使用逗号作为分隔符)来支持多个上下文。 在具有两次定义的bean的多个上下文位置的情况下,优先级最高。
namespace WebApplicationContext的命名空间。 默认为[servlet-name] -servlet。