DispatcherServlet
本章是 Spring MVC 教程的起点。Spring MVC 基于 Spring 容器运行,本教程默认必要的 Core 配置(组件扫描、依赖注入)已就绪,此后所有内容只聚焦 Web 请求处理层 的机制与注解。
DispatcherServlet 是 Spring MVC 的前端控制器(Front Controller),也是整个 Web 请求处理链路的唯一入口。理解它如何接收请求、如何分发、如何协调各组件,是掌握 Spring MVC 的根基。
定义与作用
DispatcherServlet 是前端控制器模式在 Spring MVC 中的具体实现。它之所以存在,是因为传统 Servlet 开发中每个 URL 对应一个独立 Servlet 的做法导致了代码重复和配置膨胀——每个 Servlet 都要手动处理编码、认证、日志,每增加一个接口就要在 web.xml 增加一对 servlet-mapping。DispatcherServlet 用一个中央入口替代了 N 个分散入口,将通用逻辑集中到一处,让业务处理器只关注业务本身。
DispatcherServlet 是 Spring MVC 的核心类,继承自 HttpServlet。它的职责可以用一句话概括:接收所有进入应用的 HTTP 请求,把它们分发给正确的处理器(Controller),并把处理结果包装成 HTTP 响应返回给客户端。
在传统的 Servlet 开发中,每个 URL 对应一个独立的 Servlet 类(如 /login 对应 LoginServlet,/register 对应 RegisterServlet)。Spring MVC 用 DispatcherServlet 彻底改变了这一模式——整个应用只需要一个 DispatcherServlet,它像一位"总调度员",根据请求的 URL 和方法,把请求转交给对应的 Controller 方法处理。
生活类比:医院挂号台
想象你去医院看病:
- 传统 Servlet 模式:医院有 20 个科室,每个科室门口都有一个独立的挂号窗口。你要去骨科就去骨科窗口,要去内科就去内科窗口,每个窗口只办一件事。
- DispatcherServlet 模式:医院只有一个总挂号台(DispatcherServlet)。你告诉挂号台"我要看骨科",挂号台查询科室分布表(HandlerMapping),找到骨科位置,给你一张导诊单(HandlerExecutionChain),你拿着单子去对应科室(Controller)看病。看完病后,报告单(ModelAndView)交回挂号台,挂号台帮你打印成正式报告(View渲染),最后交到你手里(HTTP响应)。
这个类比的关键在于:DispatcherServlet 不处理具体业务,它只负责"找到正确的人、把请求送过去、把结果包装好送回来"。
核心原理
请求处理完整流程
当一个 HTTP 请求到达 Spring MVC 应用时,DispatcherServlet 按以下步骤处理:
上图展示了 DispatcherServlet 的完整调度流程。注意其中 HandlerMapping、HandlerAdapter、ViewResolver 三个组件的分工——这种分工不是随意设计的,而是三种经典设计模式(策略模式、适配器模式、职责链模式)的具体体现。
上图展示了 Spring MVC 处理一个请求的完整链路。注意几个关键节点:
- HandlerMapping 负责"找对人"——根据 URL 找到对应的 Controller 方法
- HandlerAdapter 负责"能调用"——屏蔽不同 Controller 类型的调用差异
- 拦截器链 在 Controller 前后插入横切逻辑(如登录检查、日志记录)
- ViewResolver 负责"看得见"——把逻辑视图名解析为具体的页面模板
DispatcherServlet 的容器协作
DispatcherServlet 本身也是 Spring 容器中的一个 Bean。它通过容器获取所有协作组件:
重要边界:上图中的 Service、Repository、DataSource 等 Bean 也在容器中,但它们属于 Spring Core 层,本教程不展开。我们只需知道:Controller 通过依赖注入获取 Service,这条链路由容器自动完成。
适用位置与配置
DispatcherServlet 需要在 Web 应用中进行注册。Spring Boot 项目中,这一步被自动配置隐藏了;但在传统 Spring MVC 项目中,需要显式配置。
Spring Boot 自动配置(默认)
Spring Boot 会自动注册 DispatcherServlet,映射路径为 /(处理所有请求):
// 无需手动配置,Spring Boot 自动完成
// 等效于在 application.properties 中:
// server.servlet.context-path=/
传统 Spring MVC 配置(web.xml)
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</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>
| 配置项 | 含义 |
|---|---|
servlet-class | DispatcherServlet 的全限定类名 |
contextConfigLocation | Spring MVC 配置文件的位置 |
load-on-startup | 服务器启动时立即加载(而非第一次请求时) |
url-pattern | / 表示处理所有请求(静态资源除外) |
完整示例
场景
飞翔科技开发了一个员工信息管理系统。CTO 大翔要求系统使用 Spring MVC 处理所有 Web 请求。架构师白歌负责搭建 DispatcherServlet 的基础配置。
项目结构
employee-web/
├── src/main/java/
│ └── com/feixiang/web/
│ ├── EmployeeController.java # 控制器(本教程后续章节讲解)
│ └── config/
│ └── WebConfig.java # Spring MVC 配置类
├── src/main/resources/
│ └── application.properties
└── pom.xml
Spring Boot 项目(零配置)
// EmployeeController.java
package com.feixiang.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class EmployeeController {
@GetMapping("/hello")
public String hello() {
return "Hello from Spring MVC!";
}
}
# application.properties
server.port=8080
启动应用后,DispatcherServlet 自动注册,处理所有请求:
$ curl http://localhost:8080/hello
Hello from Spring MVC!
传统 Spring MVC 项目(显式配置)
// WebConfig.java
package com.feixiang.web.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@EnableWebMvc
@ComponentScan("com.feixiang.web")
public class WebConfig {
// DispatcherServlet 需要的配置
}
// WebAppInitializer.java(替代 web.xml)
package com.feixiang.web.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null; // 父容器配置(本教程不展开)
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class}; // DispatcherServlet 容器配置
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; // 映射所有请求
}
}
变化分析:
- Spring Boot 模式下,DispatcherServlet 的注册和配置完全自动化,开发者只需写 Controller
- 传统模式下,需要显式告诉 Spring"哪里是 DispatcherServlet 的配置类、映射什么路径"
- 两种模式的请求处理流程完全一致,区别只在"谁负责注册 DispatcherServlet"
易错场景与面试考点
误区一:DispatcherServlet 处理所有请求,包括静态资源
错误认知:"DispatcherServlet 映射了 /,所以 /css/style.css 也会交给 Controller 处理。"
纠正:默认情况下确实如此。Spring Boot 通过 ResourceHttpRequestHandler 自动处理静态资源;传统 Spring MVC 需要在配置中显式配置静态资源映射:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
否则访问 /css/style.css 会报 404,因为 DispatcherServlet 找不到对应的 Controller 方法。
误区二:DispatcherServlet 和 Servlet 容器是同一个东西
错误认知:"DispatcherServlet 就是 Tomcat,Tomcat 就是 DispatcherServlet。"
纠正:Tomcat 是 Servlet 容器,负责接收网络连接、解析 HTTP 协议、管理线程池。DispatcherServlet 是一个 Servlet,由 Tomcat 创建并调用其 service() 方法。关系是:Tomcat 调用 DispatcherServlet,DispatcherServlet 调用 Controller。两者是"容器"与"被容器管理的组件"的关系。
面试高频:DispatcherServlet 的工作流程
标准回答:
- 接收 HTTP 请求
- 通过 HandlerMapping 查找匹配的 Controller 方法
- 获取拦截器链(HandlerExecutionChain)
- 执行拦截器 preHandle
- 通过 HandlerAdapter 调用 Controller 方法
- 获取返回结果(ModelAndView 或数据)
- 执行拦截器 postHandle
- 通过 ViewResolver 解析视图(如果是视图渲染模式)
- 渲染视图
- 执行拦截器 afterCompletion
- 返回 HTTP 响应
小结
DispatcherServlet 是 Spring MVC 的前端控制器和唯一入口,它接收所有 HTTP 请求,通过 HandlerMapping 找到目标 Controller,通过 HandlerAdapter 执行方法,最后通过 ViewResolver 渲染响应。它不处理具体业务,只负责调度与协调。
本章与全局的关系:本章回答了"请求从哪来、怎么被分发"。下一章"控制器与请求映射"将深入讲解 Controller 如何声明请求映射规则,以及 @RequestMapping 等注解的用法。