22.2 DispatcherServlet

优质
小牛编辑
126浏览
2023-12-01

22.2 DispatcherServlet

和许多其他的Web MVC框架一样,Spring的Web MVC框架是请求驱动的,围绕一个向控制器分派请求的中央Servlet设计,并提供了其他有助于Web应用程序开发的功能。但是,Spring的DispatcherServlet不仅仅如此,它和Spring的IoC容器完全集成,因而您可以使用Spring的每一个功能。

Spring Web MVC中DispatcherServlet的请求处理工作流程如下图所示。精通模式的读者会看出DispatcherServlet是“前端控制器”设计模式的一种表述(这是Spring Web MVC与其他许多领先的Web框架共享的一种模式)。

Figure 22.1. The request processing workflow in Spring Web MVC (high level)

The request processing workflow in Spring Web MVC (high level)

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

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {
        ServletRegistration.Dynamic registration = container.addServlet("example", new DispatcherServlet());
        registration.setLoadOnStartup(1);
        registration.addMapping("/example/*");
    }

}

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

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中的声明和映射:

下面web.xml中的配置等价于上面基于代码的例子:

<web-app>
    <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>

如7.15节“ApplicationContext的附加功能”中所述,Spring中的ApplicationContext实例可以限定作用域。在Web MVC框架中,每个DispatcherServlet都有自己的WebApplicationContext,它继承了所有已经在根WebApplicationContext中定义的bean。根WebApplicationContext应该包含全部的基础组件bean,这些bean应该在其他上下文中和Servlet实例之间共享。在特定于servlet的域中,这些被继承的bean可以被覆盖,并且您可以为给定的Servlet实例定义新的特定于指定作用域的bean。

Figure 22.2. Typical context hierarchy in Spring Web MVC

The request processing workflow in Spring Web MVC (high level)

初始化DispatcherServlet时,Spring MVC会在Web应用程序的WEB-INF目录中查找名为*[servlet-name]-servlet.xml*的文件,并且创建文件中定义的bean,覆盖在全局作用域中使用相同名称描述的bean的定义。

请考虑下面的DispatcherServletServlet配置(在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)。可以通过Servlet初始化参数更改此配置文件的确切位置(详细信息请参阅下文)。

对单一DispatcherServlet的场景,也可能只有一个根上下文。

Figure 22.3. Single root context in Spring Web MVC

The request processing workflow in Spring Web MVC (high level)

这可以通过设置一个空的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>

WebApplicationContext是普通ApplicationContext的一个扩展,它具有Web应用程序所需的一些额外功能。它与普通的ApplicationContext的不同之处在于它能够解析主题(见22.9节“使用主题”),还在于它知道它与哪个Servlet相关联(有一个到ServletContext的链接)。WebApplicationContext绑定在ServletContext中,并且如果需要访问WebApplicationContext,可以通过使用RequestContextUtils类的静态方法随时找到它。

请注意,我们可以通过基于Java的配置达到同样的目的:

public class GolfingWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // GolfingAppConfig defines beans that would be in root-context.xml
        return new Class[] { GolfingAppConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        // GolfingWebConfig defines beans that would be in golfing-servlet.xml
        return new Class[] { GolfingWebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/golfing/*" };
    }

}