本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。

DispatcherServlet 简介

DispatcherServlet是 Spring MVC 框架的核心组件,充当前端控制器(Front Controller)的角色。它负责接收所有的 HTTP 请求,并将它们分发给适当的处理器(控制器)。DispatcherServlet是整个 Spring MVC 应用程序的入口点,管理请求的整个生命周期。

通过源码查看 DispatcherServlet 定义:

public class DispatcherServlet extends FrameworkServlet
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware

DispatcherServlet 继承了 FrameworkServlet,继承了 HttpServletBean,继承了 HttpServlet。HttpServlet 来自jakarta.servlet.http.HttpServlet,也就是新版 Servlet:

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0</version>
    <scope>provided</scope>
</dependency>

果然,Spring MVC 是基于 Servlet 的!

DispatcherServlet 注册

DispatcherServlet 是个 Servlet,需要注册后才能使用。有 3 种注册方式:web.xml、Spring MVC、Spring Boot

① web.xml

<web-app>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/app-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>app</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>app</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>

</web-app>

② Spring MVC

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

③ Spring Boot

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Bean
    public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(WebApplicationContext context) {
        AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
        webContext.register(AppConfig.class);

        DispatcherServlet dispatcherServlet = new DispatcherServlet(webContext);
        ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(dispatcherServlet, "/app/*");
        registration.setName("app");
        registration.setLoadOnStartup(1);
        return registration;
    }
}

Spring Boot 通常会自动配置DispatcherServlet。Spring Boot 的自动配置机制会根据应用程序的依赖和配置自动创建和注册DispatcherServlet,并将其映射到根路径(/)。这使得开发者无需手动配置DispatcherServlet,从而简化了开发过程。Spring Boot 通过spring-boot-starter-web依赖自动配置DispatcherServlet。当你在项目中引入这个依赖时,Spring Boot 会自动扫描并配置 Web 相关的组件,包括DispatcherServlet。

Context 层级

DispatcherServlet 的构造方法,参数是 WebApplicationContext。WebApplicationContext 定义如下:

package org.springframework.web.context;

import jakarta.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.lang.Nullable;

public interface WebApplicationContext extends ApplicationContext {
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
    String SCOPE_REQUEST = "request";
    String SCOPE_SESSION = "session";
    String SCOPE_APPLICATION = "application";
    String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
    String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
    String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";

    @Nullable
    ServletContext getServletContext();
}

有个 getServletContext 方法,返回了熟悉的 ServletContext,来自jakarta.servlet.ServletContext。没错,Spring MVC 的底层确实是 Servlet!

WebApplicationContext 继承自 ApplicationContext,ApplicationContext 的定义如下:

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    @Nullable
    String getId();

    String getApplicationName();

    String getDisplayName();

    long getStartupDate();

    @Nullable
    ApplicationContext getParent();

    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

在使用框架时,Context 可以是层级结构的:

Servlet WebApplicationContext 是特定于某个DispatcherServlet实例的上下文,包含特定于该 Servlet 的 Bean。Root WebApplicationContext 是整个 Web 应用程序的根上下文,包含共享的基础设施 Bean。多个DispatcherServlet实例之间共享一个 Root WebApplicationContext,每个实例都有自己的 Servlet WebApplicationContext。

Context 也可以是单一的,如果只需要一个 Context,那么配置 Root WebApplicationContext 即可。

特殊 Bean 类型

在 Spring 框架中,Bean 是 Spring IoC 容器管理的对象。特殊 Bean 通常指那些实现了特定接口或继承了特定类的 Bean,这些接口或类定义了某些框架契约。

  1. HandlerMapping

    HandlerMapping 是 Spring MVC 中用于将请求 URL 映射到相应处理器(Controller)的方法。它的主要职责是根据请求找到合适的处理器。常见实现: RequestMappingHandlerMapping:基于注解的映射,如@RequestMapping SimpleUrlHandlerMapping:基于 URL 路径的映射

  2. HandlerAdapter

    HandlerAdapter 是用于执行处理器(Controller)的方法。它的主要职责是调用处理器并返回一个ModelAndView对象。常见实现:

    RequestMappingHandlerAdapter:处理基于注解的控制器方法

    SimpleControllerHandlerAdapter:处理实现了Controller接口的控制器

  3. HandlerExceptionResolver

    HandlerExceptionResolver 用于处理在处理请求过程中抛出的异常。它的主要职责是将异常转换为适当的视图或响应。常见实现:

    ExceptionHandlerExceptionResolver:处理使用@ExceptionHandler注解的方法

    ResponseStatusExceptionResolver:处理使用@ResponseStatus注解的方法

  4. ViewResolver

    ViewResolver 用于将逻辑视图名称解析为实际的视图对象。它的主要职责是找到合适的视图来渲染模型数据。常见实现:

    InternalResourceViewResolver:解析 JSP 视图

    ThymeleafViewResolver:解析 Thymeleaf 视图

  5. LocaleResolver, LocaleContextResolver

    LocaleResolver 和 LocaleContextResolver 用于解析和设置请求的区域信息(Locale)。常见实现:

    AcceptHeaderLocaleResolver:基于请求头的区域信息

    CookieLocaleResolver:基于 Cookie 的区域信息

    TimeZoneAwareLocaleContextResolver:支持时间区域的解析,LocaleContextResolver:扩展了LocaleResolver

  6. ThemeResolver

    ThemeResolver 用于解析和设置请求的主题信息。主题通常用于定义应用的外观和感觉。常见实现:

    FixedThemeResolver:使用固定的主题

    CookieThemeResolver:基于 Cookie 的主题解析

  7. MultipartResolver

    MultipartResolver 用于处理文件上传请求。它的主要职责是将多部分请求解析为文件和表单字段。常见实现:

    CommonsMultipartResolver:基于 Apache Commons FileUpload

    StandardServletMultipartResolver:基于 Servlet 3.0 的文件上传

  8. FlashMapManager

    FlashMapManager 用于在重定向请求之间传递临时数据。它的主要职责是管理 Flash 属性。常见实现:

    SessionFlashMapManager:基于 HTTP 会话的 FlashMap 管理

关于 HandlerMapping 和 HandlerAdapter

查看它们的源码:

package org.springframework.web.servlet;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.lang.Nullable;

public interface HandlerMapping {
    String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
    /** @deprecated */
    @Deprecated
    String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    default boolean usesPathPatterns() {
        return false;
    }

    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
package org.springframework.web.servlet;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

public interface HandlerAdapter {
    boolean supports(Object handler);

    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /** @deprecated */
    @Deprecated
    long getLastModified(HttpServletRequest request, Object handler);
}

结合这个图来理解:

先看HandlerMapping这块,DispatcherServlet 接收到请求,调用 HandlerMapping 来查找合适的处理器,HandlerMapping 返回处理器及其相关的拦截器信息。

getHandler 方法返回了 HandlerExecutionChain 类,包含了 Handler 和 HandlerInterceptor:

再看HandlerAdapter这块,DispatcherServlet 调用 HandlerAdapter 来执行处理器的方法,HandlerAdapter 调用处理器的方法并返回ModelAndView对象,DispatcherServlet 根据ModelAndView对象来选择视图进行渲染。handle 方法入参 request、response、handler,返回 ModelAndView:

其中 SimpleControllerHandlerAdapter 类的源码:

package org.springframework.web.servlet.mvc;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    public SimpleControllerHandlerAdapter() {
    }

    public boolean supports(Object handler) {
        return handler instanceof Controller;
    }

    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return ((Controller)handler).handleRequest(request, response);
    }

    public long getLastModified(HttpServletRequest request, Object handler) {
        if (handler instanceof LastModified lastModified) {
            return lastModified.getLastModified(request);
        } else {
            return -1L;
        }
    }
}

DispatcherServlet 有几个方法入参为 ModelAndView,选择视图进行渲染:

感兴趣可以到源码跟踪细节:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>6.1.10</version>
</dependency>

它们分别应用了不同的设计模式:

  • HandlerMapping应用了策略模式(Strategy Pattern)

    HandlerMapping接口定义了查找处理器的策略,而具体的实现类(如RequestMappingHandlerMapping、SimpleUrlHandlerMapping等)则提供了不同的查找策略。这样,Spring MVC 可以根据配置或需求选择不同的HandlerMapping实现,而不需要修改核心代码。

  • HandlerAdapter应用了适配器模式(Adapter Pattern)

    HandlerAdapter接口定义了执行处理器的方法,而具体的实现类(如RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter等)则适配了不同类型的处理器。这样,Spring MVC 可以通过统一的接口来调用不同类型的处理器,而不需要关心处理器的具体实现细节。

123

参考资料:

https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet.html

ChatGPT

《跟开涛学 SpringMVC》