跳到主内容
创建时间: 2026-09-16最后更新: 2026-09-16

心跳接口已经能够接收请求并返回 JSON,但请求为什么会找到某个 Java 方法、参数从哪里得到、返回对象又怎样变成响应,还需要一条完整解释链路。这些工作由 Spring Web MVC 协调完成。

当前项目使用 spring-boot-starter-webmvc,运行的是基于 Servlet 的 Spring MVC。本章只观察框架处理过程;下一篇再集中编写 HealthController 的映射、参数和响应代码。

1. MVC 与 Servlet

MVC 是 Model、View、Controller 的缩写。传统服务器页面把数据放入 Model,由 View 模板渲染 HTML;当前心跳项目是 REST 接口,Controller 返回数据对象,消息转换器将它写成 JSON,不会进入模板渲染流程。

需要区分三个层次:

层次当前实现职责
Servlet 容器内嵌 Tomcat监听端口,运行 Servlet 和 Filter
Spring MVCDispatcherServlet 及其协作组件把请求分发到处理方法并组织响应
应用代码Controller、Service处理 HTTP 边界和业务规则

HealthController 不是 Servlet,它是由容器管理、再由 Spring MVC 调用的 Bean。DispatcherServlet 才是运行在 Tomcat 中的中心 Servlet。

项目通过 Starter 引入 MVC:

pom.xml
1
<dependency>
2
<groupId>org.springframework.boot</groupId>
3
<artifactId>spring-boot-starter-webmvc</artifactId>
4
</dependency>

Starter 提供 Spring MVC、JSON 转换和默认内嵌 Tomcat 所需依赖。Spring Boot 根据 classpath 启用自动配置,因此项目不需要手动创建 Tomcat、注册 DispatcherServlet 或维护 web.xml。

2. 请求管线

Spring MVC 使用前端控制器模式:进入 MVC 的请求先到 DispatcherServlet,再由它协调其他组件。

request-flow.txt
01
客户端
02
-> Tomcat 接收 HTTP 请求
03
-> Servlet Filter 链
04
-> DispatcherServlet
05
-> HandlerMapping 查找处理方法
06
-> HandlerInterceptor.preHandle
07
-> HandlerAdapter
08
-> HandlerMethodArgumentResolver 准备方法参数
09
-> 调用 Controller 方法
10
-> HandlerMethodReturnValueHandler 处理返回值
11
-> HttpMessageConverter 读写请求体或响应体
12
-> HandlerInterceptor.postHandle
13
-> HandlerInterceptor.afterCompletion
14
-> Filter 链返回
15
-> Tomcat 发送 HTTP 响应

这条管线不是每个请求都重新扫描所有类。应用启动时,MVC 已经发现控制器 Bean 并登记处理方法;请求到来后,再根据登记结果匹配。

正常返回和抛出异常会走不同分支。正常结果进入返回值处理,异常则交给 HandlerExceptionResolver 尝试转换成 HTTP 响应。只有没有机制能够处理时,异常才继续向 Servlet 容器传播。

3. 查找与调用 Handler

在 Spring MVC 中,Handler 通常是某个控制器方法,而不只是控制器类。框架会把 Bean、Java 方法和映射条件组合成可调用的处理器。

HandlerMapping 根据路径、HTTP 方法、查询条件、请求头和媒体类型等信息寻找匹配项。它只负责回答「应该处理哪个方法」,不负责直接调用。

HandlerAdapter 负责以当前 Handler 所需的方式完成调用。对注解控制器,它会进一步使用参数解析器准备 Java 参数,再调用反射得到的方法。

handler-selection.txt
1
GET /api/v1/health
2
-> 路径匹配 /api/v1/health
3
-> HTTP 方法匹配 GET
4
-> consumes / produces 等条件匹配
5
-> 确定 HealthController.health()
6
-> HandlerAdapter 调用该方法

如果没有路径匹配,通常得到 404 Not Found;路径存在但 HTTP 方法不匹配,通常是 405 Method Not Allowed;请求或响应媒体类型不满足条件,则可能得到 415 Unsupported Media Type 或 406 Not Acceptable。

映射冲突通常在应用启动时暴露。例如两个方法声明了无法区分的相同路径和 HTTP 方法,MVC 无法建立唯一映射,不应依赖声明顺序碰运气。

4. 参数与消息转换

Controller 方法只声明 Java 参数,不会自己解析原始 HTTP 字节。HandlerMethodArgumentResolver 根据参数类型和注解选择解析方式:

HTTP 数据解析结果
路径片段路径变量参数
查询字符串查询参数
请求头请求头参数
JSON 请求体Java 请求对象
Servlet 请求上下文HttpServletRequest 等基础设施类型

路径和查询参数最初通常是字符串,转换服务会把它们转换为 int、枚举、时间等目标类型。转换失败时,控制器方法不会收到一个勉强生成的值,而是进入请求错误处理流程。

请求体需要消息转换器参与。它结合 Content-Type 和目标 Java 类型选择读取方式:

request-conversion.txt
1
Content-Type: application/json
2
JSON 字节
3
-> Jackson 消息转换器
4
-> HealthCheckRequest
5
-> Bean Validation
6
-> Controller 方法参数

消息转换与参数校验是两个阶段。转换负责建立对象,Bean Validation 负责检查对象是否满足约束;JSON 语法错误与字段违反 @NotBlank 也会产生不同异常。

5. 返回值与异常

Controller 方法执行结束后,HandlerMethodReturnValueHandler 根据返回类型和注解决定下一步:

返回形式MVC 行为
REST Controller 返回普通对象使用消息转换器写入响应体
ResponseEntity<T>使用其中的状态码、响应头和响应体
普通 Controller 返回字符串通常作为视图名称
ModelAndView使用模型数据和视图信息渲染页面

REST 响应转换方向与请求相反:

response-conversion.txt
1
HealthResponse
2
-> 返回值处理器
3
-> 根据 Accept 和 produces 选择表示形式
4
-> Jackson 消息转换器
5
-> JSON 响应体

客户端的 Accept、映射声明的 produces、可用消息转换器和返回类型共同参与内容协商。接口只支持 JSON,而客户端只接受 HTML 时,框架不能凭空生成 HTML。

处理过程中抛出的异常会交给异常解析器链。控制器局部的 @ExceptionHandler、全局 @ControllerAdvice、ResponseStatusException 和框架默认异常处理都可能参与。后面的「统一异常处理」会集中实现业务异常、校验错误与 ProblemDetail 的转换。

异常处理方法返回的对象仍然经过返回值处理器和消息转换器。错误响应不是绕开 MVC 的另一套机制,而是同一管线中的失败分支。

6. Filter、Interceptor 与配置

Filter 和 HandlerInterceptor 都能在 Controller 前后执行,但所在层次不同:

对比项Servlet FilterHandlerInterceptor
所在位置Servlet 容器层Spring MVC 层
执行时机DispatcherServlet 前后Handler 已匹配之后、方法调用前后
是否知道控制器方法通常不知道可以取得 Handler 信息
常见用途请求包装、编码、安全过滤链、底层日志MVC 耗时、区域设置、处理器相关检查

身份认证与授权更适合 Spring Security 的 Filter 链,而不是只靠 MVC Interceptor。安全检查需要尽早覆盖进入应用的请求,也要处理那些尚未进入 Controller 的分支。

Spring Boot 已经配置好常用映射处理器、参数解析器、消息转换器和静态资源处理。需要增加 CORS、格式转换、Interceptor 或资源规则时,创建 WebMvcConfigurer 配置类:

src/main/java/cn/usehook/spring/config/WebConfiguration.java
01
package cn.usehook.spring.config;
02
03
import org.springframework.context.annotation.Configuration;
04
import org.springframework.web.servlet.config.annotation.CorsRegistry;
05
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
06
07
@Configuration
08
public class WebConfiguration implements WebMvcConfigurer {
09
10
@Override
11
public void addCorsMappings(CorsRegistry registry) {
12
registry.addMapping("/api/**")
13
.allowedOrigins("https://springboot.usehook.cn")
14
.allowedMethods("GET", "POST");
15
}
16
}

这种方式会在保留 Spring Boot 自动配置的基础上增加规则。不要为了添加一个扩展点就同时使用 @EnableWebMvc;它表示应用准备接管完整 MVC 配置,会改变自动配置参与方式。

现在一次请求的框架链路已经完整:Tomcat 提供 Servlet 运行环境,Filter 处理容器层逻辑,DispatcherServlet 协调 MVC,HandlerMapping 和 HandlerAdapter 找到并调用方法,参数解析器与消息转换器负责输入输出,异常解析器处理失败。下一篇只需要站在这条管线的应用边界上,专注编写 Controller。