优秀的编程知识分享平台

网站首页 > 技术文章 正文

深入剖析 Spring Boot3 中 Spring MVC 请求处理全流程

nanyue 2025-07-28 19:27:26 技术文章 1 ℃

在当今的互联网软件开发领域,Spring Boot 已然成为构建高效、可靠应用的得力框架。其中,Spring MVC 作为 Spring Boot 处理 Web 请求的核心模块,其从浏览器发送请求到进入 Controller 处理并返回数据的全流程,对于开发者而言至关重要。透彻理解这一流程,不仅能让我们编写出更健壮、高效的代码,还能在遇到问题时迅速定位并解决。接下来,就让我们一同深入探索这个充满奥秘的过程。

客户端发起请求

一切的开始,是客户端(通常为浏览器,但也可能是其他 HTTP 客户端工具)向 Spring Boot 应用的特定 URL 发送 HTTP 请求。这个请求犹如一颗投入平静湖面的石子,打破了系统的宁静,引发后续一系列的 “涟漪效应”。请求中包含了诸多关键信息,例如请求方法(GET、POST、PUT、DELETE 等)、请求头(如 User - Agent、Content - Type 等)以及请求体(若为 POST、PUT 等包含数据提交的请求)。这些信息将在后续的处理流程中发挥重要作用,引导着请求一步步被正确处理。

DispatcherServlet 接收请求

当客户端的请求抵达服务器,Spring Boot 应用中的 DispatcherServlet 便如同一位严阵以待的门卫,第一时间拦截所有请求。它在整个 Spring MVC 请求处理流程中占据着核心地位,是请求处理流程的前端控制器,肩负着将请求准确无误地转发到正确处理器,并最终返回响应的重任。DispatcherServlet 的存在,就像是交响乐团的指挥,协调着各个组件之间的协作,确保整个请求处理过程有条不紊地进行。

(一)DispatcherServlet 的初始化

在 Spring Boot 应用启动时,DispatcherServlet 会通过 Spring Boot 的自动配置机制完成初始化。这一过程涉及多个配置类,例如
DispatcherServletAutoConfiguration。该配置类负责定义一系列与 DispatcherServlet 相关的配置,包括决定是否将 DispatcherServlet 注册到 Servlet 容器、加载 DispatcherServlet 并进行相关设置等。在初始化过程中,DispatcherServlet 会创建并初始化 WebApplicationContext,这是 Spring MVC 应用的上下文环境,其中包含了应用的所有 Bean 定义以及相关配置信息。同时,DispatcherServlet 还会维护一个 HandlerMapping 列表,这个列表将在后续的请求处理中发挥关键作用,用于查找处理请求的合适处理器。

(二)请求处理

当请求到达 DispatcherServlet 时,它会从 HttpServletRequest 中提取出请求的关键信息,如请求方法、URL、参数等。这些信息就像是请求的 “身份证”,DispatcherServlet 将依据它们来判断如何处理该请求。

HandlerMapping 进行映射

HandlerMapping 如同一位精准的导航员,在 DispatcherServlet 接收到请求后,它会依据请求的 URL、请求方法等规则,在众多的处理器中查找能够处理该请求的 Controller。Spring MVC 提供了多种类型的 HandlerMapping,以满足不同的映射需求。

(一)
RequestMappingHandlerMapping

在 Spring Boot 应用中,最常用的 HandlerMapping 当属
RequestMappingHandlerMapping。它主要用于处理基于注解(如 @RequestMapping、@GetMapping、@PostMapping 等)的请求映射。当 Spring 容器初始化时,
RequestMappingHandlerMapping 作为一个 Spring Bean 被创建。其配置主要通过 WebMvcAutoConfiguration 类来完成。在初始化过程中,
RequestMappingHandlerMapping 的一些关键属性会被设置,例如:

  1. order:该属性定义了 RequestMappingHandlerMapping 在众多 HandlerMapping 中的优先级。优先级较高的 HandlerMapping 会优先处理请求。
  2. useSuffixPatternMatch、useTrailingSlashMatch 等:这些属性控制了 URL 匹配的规则。例如,useSuffixPatternMatch 决定是否支持后缀模式匹配,若设置为 true,那么对于请求 URL 为 “/user/list.html” 和 “/user/list”,在匹配处理器时可能会被视为相同的请求(具体取决于其他配置)。
  3. pathMatcher:用于配置路径匹配策略,默认情况下使用 AntPathMatcher。AntPathMatcher 支持类似 Ant 风格的路径表达式,例如 “/user/*” 可以匹配 “/user/1”“/user/2” 等所有以 “/user/” 开头的 URL 路径。

初始化完成后,
RequestMappingHandlerMapping 会扫描应用上下文中所有标有 @RequestMapping 及其衍生注解的控制器方法,并将这些方法注册为处理器。在这个过程中,它会仔细解析控制器类和方法上的注解,构建起请求到处理器的精确映射关系,包括 URL 路径、HTTP 方法、请求参数等。例如,对于一个标注了 @GetMapping ("/user/{id}") 的控制器方法,
RequestMappingHandlerMapping 会将以 “/user/” 开头且符合路径变量格式的 GET 请求映射到该方法上。

(二)其他 HandlerMapping

除了
RequestMappingHandlerMapping,Spring MVC 还提供了其他类型的 HandlerMapping,如 SimpleUrlHandlerMapping 用于简单的 URL 映射,适用于静态资源的映射场景;BeanNameUrlHandlerMapping 则根据 Bean 名称进行映射。在实际应用中,开发者可以根据具体需求选择合适的 HandlerMapping,或者通过自定义 HandlerMapping 来实现特定的映射逻辑。

Interceptor 拦截器处理(可选)

在请求从 DispatcherServlet 传递到 Controller 的途中,有可能会经过拦截器链。拦截器就像是关卡上的守卫,在请求处理的不同阶段执行特定的逻辑。与基于 Servlet 的过滤器不同,拦截器是基于 Spring 框架的,这使得它能够更好地与 Spring 的其他组件进行集成。

(一)拦截器的执行顺序

拦截器主要有三个关键方法:preHandle、postHandle 和 afterCompletion,它们的执行顺序如下:

  1. preHandle:在请求到达控制器之前执行。开发者可以在这个方法中进行一些前置处理,如日志记录、权限校验等。如果 preHandle 方法返回 false,那么请求将终止,不再继续往后传递到控制器,而是直接返回响应。例如,在一个需要用户登录才能访问某些功能的应用中,可以在 preHandle 方法中检查请求头中的用户认证信息,如果认证失败,则返回错误响应,阻止请求继续访问控制器。
  2. postHandle:在控制器方法处理完成后,但在视图渲染之前执行。这个阶段可以用于对控制器返回的数据进行一些处理,或者在视图渲染前添加一些额外的信息到模型中。
  3. afterCompletion:在整个请求完成后,包括视图渲染之后执行。通常用于进行资源清理等操作,例如关闭数据库连接、释放线程资源等。

(二)自定义拦截器示例

在 Spring Boot 应用中,开发者可以通过实现 HandlerInterceptor 接口来自定义拦截器。以下是一个简单的日志记录拦截器示例:

@Component
public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        long start = System.currentTimeMillis();
        request.setAttribute("startTime", start);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        // 可以在这个方法中对返回的数据进行处理
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        long start = (long) request.getAttribute("startTime");
        long end = System.currentTimeMillis();
        long cost = end - start;
        System.out.println("请求" + request.getRequestURI() + "耗时" + cost + "ms");
    }
}

为了使自定义拦截器生效,还需要在配置类中进行注册,例如:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");
    }
}

上述配置表示将 LogInterceptor 应用到所有的请求路径上(“/**” 表示所有路径)。

Controller 方法处理请求

经过 HandlerMapping 的映射以及拦截器(如果有的话)的处理后,请求终于抵达了 Controller。Controller 就像是业务逻辑的指挥官,负责处理请求,并调用适当的业务逻辑。在 Controller 中,通常会定义多个方法,每个方法对应着不同的请求 URL 和请求方法。这些方法会根据请求的参数和业务需求,调用 Service 层的方法来执行业务逻辑。

(一)注解驱动的控制器

在 Spring MVC 中,控制器类通常使用注解进行标识。例如,@Controller 注解用于标识一个控制器类,它处理 HTTP 请求,并返回视图或数据。而 @RestController 注解则是 @Controller 和 @ResponseBody 的组合,其方法默认返回 JSON 格式的数据,非常适合用于构建 RESTful 风格的服务。

(二)请求参数处理

Controller 方法在处理请求时,常常需要获取请求中的参数。Spring MVC 提供了多种注解来方便地获取参数,例如:

  1. @RequestParam:用于从请求中获取参数值,常用于处理 HTTP 请求中的查询参数。例如,对于请求 URL 为 “/user?name = 张三 & age=20”,在 Controller 方法中可以通过 @RequestParam ("name") String name 来获取参数 “name” 的值。
  2. @PathVariable:用于从 URL 路径中获取参数值,在 RESTful 风格的请求中应用广泛。例如,对于请求 URL 为 “/user/1”,可以在控制器方法中使用 @PathVariable ("id") Long id 来获取路径中的 “1” 作为参数值。
  3. @RequestBody:用于将请求体中的数据绑定到方法的参数上,通常用于处理 POST 请求中的请求体数据。例如,当客户端发送一个 JSON 格式的请求体时,可以通过 @RequestBody User user 将请求体中的数据映射到 User 对象上。

(三)数据绑定与验证(可选)

如果请求中包含数据,Spring Boot 会尝试将数据绑定到相应的参数上。在这个过程中,如果参数类型与请求数据类型不匹配,可能会触发类型转换。同时,Spring MVC 还支持数据验证功能。开发者可以使用 JSR - 303/JSR - 380 规范的验证注解(如 @NotNull、@Size 等)对参数进行验证。例如,在一个 User 对象的属性上添加 @NotNull 注解,当请求数据绑定到 User 对象时,如果该属性为空,验证将失败,Spring MVC 会生成相应的错误信息。这些错误信息可以通过 BindingResult 对象获取,开发者可以根据验证结果返回合适的响应,如错误提示页面或错误 JSON 数据。

返回数据或视图

Controller 方法处理完请求并执行业务逻辑后,会返回数据或者视图名给 DispatcherServlet。返回的数据类型和方式取决于具体的业务需求以及控制器方法的定义。

(一)返回视图名

如果 Controller 方法返回的是视图名,例如 “user/list”,这意味着该方法希望 DispatcherServlet 通过视图解析器来找到对应的视图并进行渲染。视图名通常与具体的视图技术相关,如 JSP、Thymeleaf 模板等。在这种情况下,Controller 方法可能还会将一些模型数据添加到 ModelAndView 对象中,这些数据将在视图渲染时被填充到视图中。

(二)返回数据

在许多情况下,尤其是在构建 RESTful API 时,Controller 方法会直接返回数据,如 JSON 格式的数据。例如,返回一个 User 对象,Spring MVC 会自动将其转换为 JSON 格式并作为 HTTP 响应体返回给客户端。这一过程涉及到 HttpMessageConverter,它负责将 Java 对象转换为适合在 HTTP 响应中传输的格式(如 JSON、XML 等),同时也能将 HTTP 请求中的数据转换为 Java 对象。

ViewResolver 解析视图(若返回视图名)

当 Controller 方法返回视图名时,DispatcherServlet 会将这个视图名交给 ViewResolver 进行解析。ViewResolver 就像是一位知识渊博的翻译官,根据视图名找到具体的视图对象。Spring MVC 支持多种视图技术,相应地也有多种 ViewResolver 实现类。

(一)
InternalResourceViewResolver

对于使用 JSP 作为视图技术的应用,通常会使用
InternalResourceViewResolver。它的作用是将视图名解析为一个 InternalResourceView 对象,该对象实际上是对 JSP 页面的封装。例如,当视图名为 “user/list” 时,
InternalResourceViewResolver 会根据配置的前缀(如 “/WEB - INF/views/”)和后缀(如 “.jsp”),将视图名解析为 “/WEB - INF/views/user/list.jsp”,并返回对应的视图对象。

(二)ThymeleafViewResolver

如果应用使用 Thymeleaf 作为视图技术,则会使用 ThymeleafViewResolver。ThymeleafViewResolver 会根据 Thymeleaf 模板的相关配置,将视图名解析为对应的 Thymeleaf 视图对象。Thymeleaf 具有强大的模板引擎功能,支持在模板中进行数据渲染、条件判断、循环等操作,能够生成动态的 HTML 页面。

视图渲染(若返回视图名)

视图解析器找到具体的视图对象后,视图渲染过程便开始了。视图对象就像是一位技艺精湛的工匠,将模型数据填充到视图中,生成最终的响应结果。

(一)JSP 视图渲染

以 JSP 视图为例,在渲染过程中,JSP 引擎会读取 JSP 页面的内容,并将 Controller 方法传递过来的模型数据填充到相应的位置。例如,如果模型数据中包含一个用户列表,JSP 页面可以通过 EL 表达式(如 ${userList})来获取这个列表,并在页面中通过循环标签(如 < c:forEach>)将用户信息展示出来。最终,JSP 引擎会将填充好数据的页面转换为 HTML 格式的字符串。

(二)Thymeleaf 视图渲染

对于 Thymeleaf 视图,它会根据模板中的语法规则,将模型数据与模板进行结合。Thymeleaf 支持在 HTML 标签中直接嵌入表达式,如 “用户姓名”,在渲染时,Thymeleaf 会将 ${user.name} 替换为实际的用户姓名。Thymeleaf 还支持条件渲染、片段包含等功能,使得视图渲染更加灵活和高效。

响应返回给客户端

经过一系列的处理,包括 DispatcherServlet 的调度、HandlerMapping 的映射、Controller 的业务处理、视图解析和渲染等,最终的响应结果被生成。DispatcherServlet 会将这个响应结果通过 HttpServletResponse 返回给客户端。如果是构建 RESTful API,返回的可能是 JSON 或其他数据格式;如果是 Web 应用,返回的则是 HTML 页面。客户端接收到响应后,会根据响应的内容进行相应的展示或处理。

总结

至此,Spring Boot3 中 Spring MVC 从浏览器发送一个请求之后,到进入 Controller 的处理返回数据的全流程就完整地走完了。理解这个全流程,对于我们优化应用性能、进行问题排查以及实现定制化的功能都具有重要的意义。在实际的开发过程中,我们可以根据具体的业务需求和场景,灵活运用 Spring MVC 的各个组件,打造出高效、稳定、可靠的互联网软件应用。

最近发表
标签列表