基于本文回答
0
评论

Spring MVC处理一个HTTP请求的完整流程

知识点图片

本文详述了Spring MVC的请求处理流程。以DispatcherServlet为核心,协调HandlerMapping、HandlerAdapter、ViewResolver等组件,完成从请求分发、控制器处理到视图渲染的完整周期,并提及REST API的特殊情况。

我们来详细分解一下Spring MVC处理一个HTTP请求的完整流程。这个流程是Spring MVC的核心,理解它对于深入使用和排查问题至关重要。

整个流程基于经典的前端控制器(Front Controller)设计模式,其中DispatcherServlet扮演着核心调度员的角色。

核心组件概览

在深入流程之前,先了解一下几个关键角色:

  1. DispatcherServlet:前端控制器。所有请求的入口,负责接收请求并将其分派给其他组件进行处理。
  2. HandlerMapping:处理器映射器。根据请求的URL、HTTP方法等信息,查找并返回处理该请求的处理器(Handler),通常是一个Controller方法,并封装成一个HandlerExecutionChain对象。
  3. HandlerAdapter:处理器适配器。DispatcherServlet不直接调用Controller方法,而是通过HandlerAdapter来调用。这样做的好处是解耦,使得DispatcherServlet可以支持任意类型的处理器。
  4. Handler (Controller):处理器。即我们编写的Controller类及其方法,负责处理具体的业务逻辑。
  5. ModelAndView:模型和视图。一个容器对象,封装了要渲染到视图中的数据(Model)和逻辑视图名(View Name)。
  6. ViewResolver:视图解析器。根据Controller返回的逻辑视图名(如 "user/list"),解析成一个具体的View对象(如一个JSP文件或Thymeleaf模板)。
  7. View:视图。负责将模型数据渲染成最终的HTML或其他格式的响应,并写回给客户端。

请求处理流程图

为了更直观地理解,可以想象下面这个流程图:

plaintext
+--------+      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模式(如 /)的请求都转发给DispatcherServletDispatcherServlet是整个流程的入口。

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中拦截器的preHandlepostHandle方法。

5. Controller (Handler) 处理业务逻辑

HandlerAdapter最终调用我们自己编写的Controller方法。在这个方法里,我们执行业务逻辑,与Service层和DAO层交互,准备模型数据。

6. Controller 返回 ModelAndView

Controller方法执行完毕后,会返回一个结果。这个结果有多种形式:

  • ModelAndView 对象:最经典的方式,包含了模型数据和逻辑视图名。
  • String:只返回一个逻辑视图名,模型数据可以由方法的ModelModelMap参数来填充。
  • 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对象给DispatcherServletView是一个接口,有多种实现,如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. 步骤1-5 和上面完全一样。
  2. 步骤6 (变化点):Controller方法返回一个POJO、List、Map等对象。由于@ResponseBody的存在,HandlerAdapter不会尝试将这个结果封装成ModelAndView
  3. 绕过视图解析HandlerAdapter会识别到@ResponseBody,并激活HttpMessageConverter(如MappingJackson2HttpMessageConverter)。
  4. HttpMessageConverter 会将Controller返回的Java对象序列化成指定的格式(通常是JSON字符串)。
  5. 序列化后的内容直接被写入HttpServletResponse的响应体中。
  6. 流程跳过了步骤7、8、9(视图解析和渲染),直接返回响应。这使得处理REST API变得非常高效。

总结

Spring MVC的请求处理流程是一个设计精良、高度解耦的责任链模式。DispatcherServlet作为中心枢纽,通过HandlerMappingHandlerAdapterViewResolver等策略接口的实现,将请求处理的各个阶段委托给专门的组件,使得整个框架灵活、可扩展。

右滑查看面试常问