一个 HTTP 请求进入 Spring MVC 应用后,大致经历了哪些主要步骤?

直接进入干货分享环节:假设我们的 Spring MVC 应用配置了 DispatcherServlet 作为前端控制器,并且映射路径为 /

  1. 请求到达 Web 容器 (如 Tomcat):

    • 客户端(例如浏览器)发送一个 HTTP 请求(比如 GET /myapp/users/123)。
    • Web 容器(Tomcat, Jetty 等)接收到这个请求。
  2. Web 容器路由到 DispatcherServlet:

    • Web 容器根据应用的部署描述符 (web.xml 或 Java Servlet 容器初始化器配置) 中 DispatcherServlet<servlet-mapping>(例如 //app/*),将该请求交给对应的 DispatcherServlet 实例处理。
  3. DispatcherServlet 接收请求:

    • DispatcherServlet 作为请求的统一入口点,开始处理该请求。
  4. 查找 Handler (处理器):

    • DispatcherServlet 查询其配置的所有 HandlerMapping 实现(按顺序)。
    • HandlerMapping 的任务是根据请求的信息(如 URL 路径 /users/123,HTTP 方法 GET 等)查找能够处理该请求的 Handler(通常是一个 Controller 类中的特定方法,封装为 HandlerMethod 对象)。
    • 如果找到合适的 Handler,HandlerMapping 会返回一个 HandlerExecutionChain 对象。这个对象不仅包含了找到的 Handler 本身,还包含了应用于该 Handler 的所有拦截器 (HandlerInterceptor) 列表。
    • 如果没有找到 Handler,通常会返回 404 Not Found 错误。
  5. 获取 HandlerAdapter (处理器适配器):

    • DispatcherServlet 拿到了 HandlerExecutionChain 后,需要调用其中的 Handler。但 Handler 的类型可能多种多样(例如基于注解的 HandlerMethod,旧式的实现了 Controller 接口的类等)。
    • 为了以统一的方式调用不同类型的 Handler,DispatcherServlet 会查询其配置的所有 HandlerAdapter 实现。
    • 它会找到支持当前 Handler 类型(例如 HandlerMethod)的那个 HandlerAdapter(例如 RequestMappingHandlerAdapter)。
  6. 执行拦截器的 preHandle 方法:

    • HandlerAdapter 在真正调用 Handler 方法之前,会按照 HandlerExecutionChain 中定义的顺序,依次调用所有拦截器的 preHandle(request, response, handler) 方法。
    • 这些拦截器可以执行一些预处理逻辑,如权限检查、日志记录等。
    • 如果任何一个 preHandle 方法返回 false,则请求处理流程在此中断,DispatcherServlet 会认为请求已被处理(拦截器可能直接生成了响应),然后会反向执行已执行过的拦截器的 afterCompletion 方法,然后结束。
  7. 调用 Handler (Controller 方法):

    • 如果所有拦截器的 preHandle 都返回 trueHandlerAdapter 就会调用 Handler(即 Controller 方法)。
    • 在调用之前,HandlerAdapter 内部会利用 HandlerMethodArgumentResolver 来解析 Controller 方法的参数。这包括:
      • 从请求中提取数据(如 @RequestParam, @PathVariable, @RequestBody 等)。
      • 进行数据绑定(将请求参数映射到方法参数对象)。
      • 进行数据类型转换。
      • 执行数据校验(如果使用了 @Valid 等)。
    • Controller 方法执行应用程序的核心业务逻辑,可能会调用 Service 层、与数据库交互等。
  8. Handler 方法返回结果:

    • Controller 方法执行完毕后会返回一个结果。这个结果可能是:
      • ModelAndView 对象:包含了逻辑视图名和模型数据。
      • String:通常代表逻辑视图名。
      • void:表示 Controller 自己处理了响应(例如直接操作 HttpServletResponse)。
      • 一个普通对象 (POJO):如果方法或类上有 @ResponseBody 注解,这个对象会被序列化后写入响应体。
      • ResponseEntity:可以更精细地控制响应状态码、头和响应体。
      • 其他(如 Callable, DeferredResult 用于异步处理)。
  9. 执行拦截器的 postHandle 方法:

    • HandlerAdapter 在成功调用完 Handler 方法后(但在视图渲染之前),会按照 HandlerExecutionChain 中定义的逆序 ,依次调用所有拦截器的 postHandle(request, response, handler, modelAndView) 方法。
    • 这些拦截器可以修改 ModelAndView 对象(例如添加一些公共的模型属性)或执行其他后处理逻辑。
    • 注意: 如果 Handler 方法执行过程中抛出了异常,postHandle 方法不会被执行。
  10. 处理 Handler 结果 / 视图解析 (如果需要):

    • DispatcherServlet 接收 HandlerAdapter 返回的结果。
    • 如果结果是 ModelAndView 或 String (逻辑视图名):
      • DispatcherServlet 会查询所有配置的 ViewResolver 实现(按顺序)。
      • ViewResolver 根据逻辑视图名解析得到一个具体的 View 接口的实例(如 JstlView, ThymeleafView)。这个 View 对象知道如何渲染特定的视图技术(如 JSP, Thymeleaf)。
    • 如果结果是 @ResponseBodyResponseEntity:
      • 这个步骤会被跳过。结果会直接交给后续步骤进行响应体写入。
  11. 视图渲染 (如果需要):

    • 如果上一步解析得到了 View 实例,DispatcherServlet 会调用该 View 实例的 render(model, request, response) 方法。
    • View 会使用传递过来的模型数据 (Model) 来渲染最终的输出(例如,JSP 引擎执行 JSP 文件生成 HTML)。
    • 渲染结果会被写入 HttpServletResponse 的输出流。
  12. 响应体写入 (对于 @ResponseBody / ResponseEntity):

    • 如果 Controller 返回的是需要直接写入响应体的对象 (@ResponseBodyResponseEntity 的 Body),DispatcherServlet (或者说 HandlerAdapter 内部的 HandlerMethodReturnValueHandler) 会使用注册的 HttpMessageConverter (如 MappingJackson2HttpMessageConverter) 将该对象序列化(例如转为 JSON 字符串)并写入 HttpServletResponse 的输出流。
  13. 执行拦截器的 afterCompletion 方法:

    • 无论请求处理是否成功(即无论是否发生异常,只要对应的 preHandle 返回了 true),在视图渲染完成或响应体写入完成后,DispatcherServlet 都会按照 HandlerExecutionChain 中定义的逆序 ,依次调用所有拦截器的 afterCompletion(request, response, handler, ex) 方法。
    • 这个方法通常用于资源清理工作(例如释放某些资源)。参数 ex 会包含处理过程中发生的异常(如果没有异常则为 null)。
  14. 异常处理:

    • 如果在上述任何步骤(除了拦截器的 preHandle 返回 false 之后)发生了异常,DispatcherServlet 会捕获这个异常。
    • 它会查询所有配置的 HandlerExceptionResolver 实现,找到能够处理该异常的 Resolver。
    • HandlerExceptionResolver 会处理异常,可能会将用户导向一个错误页面,或者返回一个包含错误信息的特定响应(例如 JSON 格式的错误信息)。
  15. 响应返回给客户端:

    • DispatcherServlet 将最终的 HTTP 响应(包含状态码、头信息、响应体)通过 Web 容器返回给客户端。

这个流程虽然看起来复杂,但每个组件职责清晰,使得 Spring MVC 框架非常灵活和可扩展。DispatcherServlet 在其中扮演了至关重要的中央协调者角色。

相关推荐
北城以北88888 小时前
Spring定时任务与Spring MVC拦截器
spring boot·spring·mvc
WizLC8 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Mr.朱鹏8 小时前
SQL深度分页问题案例实战
java·数据库·spring boot·sql·spring·spring cloud·kafka
星星不打輰9 小时前
SSM项目--SweetHouse 甜蜜蛋糕屋
java·spring·mybatis·ssm·springmvc
Neolnfra13 小时前
渗透测试标准化流程
开发语言·安全·web安全·http·网络安全·https·系统安全
fiveym13 小时前
Apache HTTP 服务搭建全攻略
网络协议·http·apache
一 乐14 小时前
办公系统|基于springboot + vueOA办公管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
2501_9167665415 小时前
【SpringMVC】实现文件上传
java·spring
她说..15 小时前
Spring AOP场景4——事务管理(源码分析)
java·数据库·spring boot·后端·sql·spring·springboot
2501_9151063215 小时前
HTTP 协议详解,HTTP 协议在真实运行环境中的表现差异
网络·网络协议·http·ios·小程序·uni-app·iphone