Spring MVC处理一个HTTP请求的完整流程
本文详述了Spring MVC的请求处理流程。以DispatcherServlet为核心,协调HandlerMapping、HandlerAdapter、ViewResolver等组件,完成从请求分发、控制器处理到视图渲染的完整周期,并提及REST API的特殊情况。
我们来详细分解一下Spring MVC处理一个HTTP请求的完整流程。这个流程是Spring MVC的核心,理解它对于深入使用和排查问题至关重要。
整个流程基于经典的前端控制器(Front Controller)设计模式,其中DispatcherServlet扮演着核心调度员的角色。
核心组件概览
在深入流程之前,先了解一下几个关键角色:
DispatcherServlet:前端控制器。所有请求的入口,负责接收请求并将其分派给其他组件进行处理。HandlerMapping:处理器映射器。根据请求的URL、HTTP方法等信息,查找并返回处理该请求的处理器(Handler),通常是一个Controller方法,并封装成一个HandlerExecutionChain对象。HandlerAdapter:处理器适配器。DispatcherServlet不直接调用Controller方法,而是通过HandlerAdapter来调用。这样做的好处是解耦,使得DispatcherServlet可以支持任意类型的处理器。Handler(Controller):处理器。即我们编写的Controller类及其方法,负责处理具体的业务逻辑。ModelAndView:模型和视图。一个容器对象,封装了要渲染到视图中的数据(Model)和逻辑视图名(View Name)。ViewResolver:视图解析器。根据Controller返回的逻辑视图名(如 "user/list"),解析成一个具体的View对象(如一个JSP文件或Thymeleaf模板)。View:视图。负责将模型数据渲染成最终的HTML或其他格式的响应,并写回给客户端。
请求处理流程图
为了更直观地理解,可以想象下面这个流程图:
+--------+ 1. 请求 +---------------------+ 2. 查找Handler +----------------+
| Client | ----------------> | DispatcherServlet | ----------------------> | HandlerMapping |
+--------+ +---------------------+ +----------------+
^ | |
| | 3. 返回HandlerExecutionChain |
| | (含Handler和拦截器) |
| v v
+---------------------+ 4. 调用Handler +----------------+
| HandlerAdapter | <---------------------- | DispatcherServlet|
+---------------------+ +----------------+
| ^ ^
| | 5. 执行Controller方法 |
| | | 7. 返回ModelAndView
v | |
+------------+ |
| Controller | --------------------------------------+
| (Handler) | 6. 业务处理, 返回ModelAndView
+------------+
|
+--------+ 11. 响应 +---------------------+ 9. 返回View对象 +----------------+
| Client | <----------------- | DispatcherServlet | <---------------------- | ViewResolver |
+--------+ +---------------------+ 8. 解析View +----------------+
|
| 10. 渲染视图
v
+-----------+
| View |
+-----------+
详细步骤分解
下面是详细的、按顺序的步骤分解:
1. 客户端发起请求
用户在浏览器中输入URL,或者通过任何HTTP客户端发送一个请求(例如 GET /users/1)。该请求首先被Web容器(如Tomcat)接收。
2. DispatcherServlet 拦截请求
Web容器根据web.xml或Java配置(WebApplicationInitializer),将所有匹配特定URL模式(如 /)的请求都转发给DispatcherServlet。DispatcherServlet是整个流程的入口。
3. HandlerMapping 查找处理器
DispatcherServlet 接收到请求后,会调用其内部配置的HandlerMapping来确定哪个Handler(即哪个Controller的哪个方法)应该处理这个请求。
HandlerMapping的实现类(如RequestMappingHandlerMapping)会检查请求的URL、HTTP方法、请求参数、Headers等信息。- 它会扫描所有被
@Controller或@RestController注解的类,找到与请求匹配的@RequestMapping(或@GetMapping,@PostMapping等)注解的方法。 - 如果找到匹配的处理器,
HandlerMapping会将其与一系列的拦截器(HandlerInterceptor)封装成一个HandlerExecutionChain对象,并返回给DispatcherServlet。如果找不到,则会抛出异常。
4. HandlerAdapter 执行处理器
DispatcherServlet 拿到了HandlerExecutionChain后,会选择一个合适的HandlerAdapter来执行这个处理器。
DispatcherServlet会遍历所有已注册的HandlerAdapter,询问它们是否支持(supports)当前的Handler类型。- 例如,
RequestMappingHandlerAdapter支持处理被@RequestMapping注解的方法。 HandlerAdapter在调用Controller方法之前,会做很多“准备工作”,比如:- 数据转换:将请求中的字符串参数转换为方法签名中指定的类型(如
Integer,Date)。 - 数据绑定:将请求参数绑定到方法的参数对象(JavaBean)上。
- 数据验证:如果配置了,会执行JSR-303数据校验。
- 解析
@RequestParam,@PathVariable,@RequestBody等注解。
- 数据转换:将请求中的字符串参数转换为方法签名中指定的类型(如
- 在调用处理器方法之前和之后,会依次执行
HandlerExecutionChain中拦截器的preHandle和postHandle方法。
5. Controller (Handler) 处理业务逻辑
HandlerAdapter最终调用我们自己编写的Controller方法。在这个方法里,我们执行业务逻辑,与Service层和DAO层交互,准备模型数据。
6. Controller 返回 ModelAndView
Controller方法执行完毕后,会返回一个结果。这个结果有多种形式:
ModelAndView对象:最经典的方式,包含了模型数据和逻辑视图名。String:只返回一个逻辑视图名,模型数据可以由方法的Model或ModelMap参数来填充。void:不返回任何东西,可能方法内部直接通过HttpServletResponse对象输出了响应,或者视图名是根据请求URL约定的。@ResponseBody/ResponseEntity:用于RESTful API,表示返回的不是视图名,而是要直接写入响应体的数据(如JSON或XML)。我们稍后会单独讨论这种情况。
HandlerAdapter将Controller的返回结果统一封装成一个ModelAndView对象,并返回给DispatcherServlet。(即使Controller返回的是String,HandlerAdapter也会把它包装成ModelAndView)。
7. ViewResolver 解析视图
DispatcherServlet 收到ModelAndView后,会将其中的逻辑视图名(如 "user/profile")交给ViewResolver(视图解析器)处理。
DispatcherServlet可以配置一个或多个ViewResolver。它们会按顺序尝试解析视图名。- 例如,
InternalResourceViewResolver会将逻辑视图名添加前缀(如/WEB-INF/views/)和后缀(如.jsp),最终解析成一个具体的物理视图路径(如/WEB-INF/views/user/profile.jsp)。 ViewResolver返回一个View对象给DispatcherServlet。View是一个接口,有多种实现,如JstlView(用于JSP)、ThymeleafView(用于Thymeleaf)等。
8. View 渲染
DispatcherServlet 拿到了View对象和模型数据(从ModelAndView中获取),然后调用View对象的render()方法。
render()方法会接收模型数据(Map形式)。View对象(如JSP页面或Thymeleaf模板引擎)利用模型数据,生成最终的HTML内容。
9. 返回响应
View渲染完成后,将生成的HTML内容写入HttpServletResponse的输出流中。
10. DispatcherServlet收尾
- 在视图渲染完成后,
DispatcherServlet会调用拦截器的afterCompletion方法,用于资源清理等收尾工作。 - 至此,整个请求-响应周期结束,Web容器将HTTP响应发送回客户端浏览器。
特殊情况:处理RESTful API(@ResponseBody)
当Controller方法被@ResponseBody注解时,流程会发生一些变化,变得更短。
- 步骤1-5 和上面完全一样。
- 步骤6 (变化点):Controller方法返回一个POJO、List、Map等对象。由于
@ResponseBody的存在,HandlerAdapter不会尝试将这个结果封装成ModelAndView。 - 绕过视图解析:
HandlerAdapter会识别到@ResponseBody,并激活HttpMessageConverter(如MappingJackson2HttpMessageConverter)。 HttpMessageConverter会将Controller返回的Java对象序列化成指定的格式(通常是JSON字符串)。- 序列化后的内容直接被写入
HttpServletResponse的响应体中。 - 流程跳过了步骤7、8、9(视图解析和渲染),直接返回响应。这使得处理REST API变得非常高效。
总结
Spring MVC的请求处理流程是一个设计精良、高度解耦的责任链模式。DispatcherServlet作为中心枢纽,通过HandlerMapping、HandlerAdapter、ViewResolver等策略接口的实现,将请求处理的各个阶段委托给专门的组件,使得整个框架灵活、可扩展。