【Spring】一文了解SpringMVC的核心功能及工作流程,以及核心组件及注解
概述
- 定义:用于构建 Web 应用程序的框架,MVC 代表 Model-View-Controller 模式
- 核心思想:将 “请求处理”、“视图渲染”、“数据模型” 分离,使得职责清晰、代码可维护
- MVC
- Model:也就是开发中的 Mapper 层,表示对模型和数据库的操作,也可以带上 Service,因为 Service 是业务逻辑
- View:是 HTML 部分,在前后端分离的架构中属于 VUE 和 React 的职责(传统架构中的 Spring 也可以负责 View,比如 JSP/Thymeleaf)
- Controller:也就是开发中的 Controller 层,负责接收 View 的请求并进行响应
- 核心功能
- 请求处理:接收和处理 HTTP 请求,支持各种 HTTP 方法(GET、POST、PUT、DELETE 等)
- URL 映射:通过注解(如 @RequestMapping)将 URL 映射到对应的处理方法
- 数据绑定:自动将请求参数绑定到 Java 对象,支持复杂的数据类型转换
- 视图解析:支持多种视图技术(JSP、Thymeleaf 等),也支持 RESTful API 的 JSON 响应
- 验证框架:集成了强大的数据验证功能,支持 JSR-303 标准
- 文件上传:内置的文件上传支持,可以轻松处理多文件上传
- 异常处理:提供统一的异常处理机制,支持自定义错误页面和异常处理器
- 组成部分
DispatcherServlet
:核心的中央处理器,负责协调整个请求的处理流程(接收客户端的请求,将请求分发给适当的组件处理,并最终返回响应)Handler
:请求处理器(Controller),执行实际的业务逻辑处理,并生成处理结果(通常会返回一个 ModelAndView 或在前后端分离项目中返回 JSON 数据)HandlerMapping
:处理器映射器,根据请求的 URL 匹配并查找能够处理该请求的 Handler,同时将相关的拦截器信息一并封装HandlerAdapter
:处理器适配器,在 DispatcherServlet 和 Handler 之间提供一个适配层,使得不同类型的 Handler 都能被统一处理ViewResolver
:视图解析器,在前后端不分离的项目中根据 Handler 返回的逻辑视图名解析为实际的视图(如 JSP 模板或 Thymeleaf),并将视图渲染为最终的 HTML 页面返回给客户端
工作流程
- 发送请求:浏览器或客户端发送 HTTP 请求到服务器
- 拦截请求:所有的请求首先会被前端控制器
DispatcherServlet
(用于协调整个请求的处理流程)拦截 - 映射请求:
DispatcherServlet
根据请求 URL,使用HandlerMapping
找到处理这个请求的合适的控制器(Controller) - 处理请求:Controller 开始处理业务逻辑(通常会调用服务层和数据访问层)
- 返回 ModelAndView:Controller 返回一个
ModelAndView
对象,这个对象包含了视图名和模型数据 - 解析视图:
DispatcherServlet
会使用ViewResolver
来解析ModelAndView
中的视图名,找到合适的视图(通常是一个 JSP、Thymeleaf 模板等) - 渲染视图:视图解析后,
DispatcherServlet
会将模型数据传递给视图进行渲染,最终生成 HTML 页面 - 返回响应给客户端:渲染后的视图会被返回给客户端,显示给用户
前后端模式
前后端分离
-
目标:后端专注于数据处理和业务逻辑,通过 RESTful API 提供 JSON 类型的数据服务,而前端负责页面渲染和用户交互
-
优点:使得前后端能够独立开发、测试和部署,提高了开发效率和系统的可维护性
-
特点:只需要返回 JSON 格式的数据,不再需要 ViewResolver / View 等组件,这部分交给前端进行处理
-
组成部分
- Controller:接收前端请求,处理请求,响应数据
- Service:用于处理具体业务逻辑的接口类,由 ServiceImpl 类实现 Service 接口中的方法
- Dao:Data Access Object,提供一个数据库操作的途径,包含用于访问数据库的方法,使得应用程序不直接与数据库交互
-
工作流程
-
Controller 中注入 Service 依赖,Service 中注入 Mapper 依赖
-
Controller 接收客户端请求,调用 Service 的逻辑方法
-
ServiceImpl 实现 Service 接口,调用 Mapper 的数据库访问功能
-
Mapper 返回数据或执行结果给 ServiceImpl 实现类
-
ServiceImpl 返回数据或执行结果给 Controller
-
Controller 返回 Response 给客户端
controller 发出请求,service提供实现逻辑,dao 层提供数据支持
-
-
流程图
前后端不分离
-
定义:Controller + Thymeleaf 引擎(视图层)
-
流程图
MVC 配置
默认配置
- 默认的静态资源路径:默认从 resources 下的
static
、public
、META_INF/static
、resources
文件夹中获取静态资源,并且可以直接访问 - 数据类型转换和格式化:通过自动注册 Converter / GenericConverter / Formatter 组件
- 返回 JSON 数据类型:通过 HttpMessageConverters 转换器,并通过 @ResponseBody 注解声明
- 自动实现 消息处理、数据绑定、类型转换、数据校验 等功能(通过 ConfigurableWebBindingInitializer )
- 包含 ContentNegotiatingViewResolver 和 BeanNameViewResolver 组件,方便视图解析(不常用,因为现在已经前后端分离了)
- 提供国际化和错误消息处理
手动配置
- 完全手动配置:自定义一个 @Configuration 类,继承 WebMvcConfigurer 接口并标注 @EnableWebMvc 禁用默认配置
- ⭐ 部分手动配置:最佳实践,写一个 @Configuration 配置类,实现 WebMvcConfigurer 接口,不标注 @EnableWebMvc 注解(因为是部分自定义,其他使用默认配置)
- 工作原理
- WebMvcAutoConfiguration 是一个自动配置类,里面有一个 EnableWebMvcConfiguration 继承了 DelegatingWebMvcConfiguration
- DelegatingWebMvcConfiguration 利用 DI 将容器中所有 WebMvcConfigurer 注入到容器中,实现了所有的 WebMvc 配置
- 用户用配置类继承 WebMvcConfigurer 会将这个 WebMvcConfigurer 注册到容器中
- Spring 调用 DelegatingWebMvcConfiguration 的方法配置底层规则时,DelegatingWebMvcConfiguration 负责加载了用户自定义的规则部分
WebMvcConfigurer
-
定义:一个用于自定义 Spring MVC 配置的接口
-
功能:根据需求来扩展或定制 Spring MVC 的默认配置,比如添加拦截器、格式化器、消息转换器等
-
使用方法:实现 WebMvcConfigurer 接口并覆盖其中的方法
-
调用时间:当 Spring Boot 启动时,Spring 容器会自动检测并调用所有实现了
WebMvcConfigurer
接口的配置类,并执行其定义的方法- 注意:由于这是 Spring Boot 的初始化配置,所以 JRebel 也无法及时更新这部分已经加载到 IOC 容器中的配置,需要重新启动项目才能生效
-
常见实现场景
- 添加拦截器:通过实现
addInterceptors(InterceptorRegistry registry)
来注册自定义拦截器。 - 配置视图解析器:通过
addViewControllers(ViewControllerRegistry registry)
增加简单的视图映射。 - 扩展消息转换器:使用
configureMessageConverters(List<HttpMessageConverter> converters)
来添加或修改消息转换器。 - 配置路径匹配策略:通过
configurePathMatch(PathMatchConfigurer configurer)
来设置路径匹配规则。
- 添加拦截器:通过实现
-
注意:如果使用 Spring Boot,Spring 容器会自动检测实现了 WebMvcConfigurer 的配置类,无需额外配置。
-
常用方法
返回值类型 命令 功能 void addInterceptors(InterceptorRegistry registry)
注册拦截器到 Spring 容器 void addViewControllers(ViewControllerRegistry registry)
添加视图控制器 void configureContentNegotiation(ContentNegotiationConfigurer configurer)
配置内容协商策略 void configureMessageConverters(List<HttpMessageConverter<?>> converters)
配置消息转换器 void configurePathMatch(PathMatchConfigurer configurer)
配置路径匹配选项
WebMvcAutoConfiguration
- 定义:WebMvcAutoConfiguration 是 Spring Boot 的自动配置类,用于配置 Spring MVC 的默认行为
- 功能
- 提供 Spring MVC 的默认配置,包括视图解析器、静态资源处理、消息转换器等
- 支持自动配置 ViewResolver、静态资源映射、welcome 页面等
- 配置默认的 HttpMessageConverters,用于处理 HTTP 请求和响应的转换
- 生效条件
- @ConditionalOnWebApplication:当前应用是一个 Web 应用(WebApplication)
- @ConditionalOnClass:类路径下存在 Servlet API 和 Spring MVC
- @ConditionalOnMissingBean:没有使用 @EnableWebMvc 注解(使用该注解会完全接管 Spring MVC 的配置)
- 主要组件配置
ContentNegotiatingViewResolver
:处理视图解析BeanNameViewResolver
:通过 Bean 名称解析视图InternalResourceViewResolver
:处理 JSP 视图解析MultipartResolver
:处理文件上传
- 静态资源配置
- 默认的静态资源位置:/static、/public、/resources、/META-INF/resources
- 静态资源缓存策略
- 欢迎页配置:index.html
核心组件
DispatcherServlet
- 定义:中央调度 Servlet,是 Servlet 的老大、Spring MVC 的核心组件,充当中央调度器的角色,用于协调整个请求处理流程
- 功能:负责接收 HTTP 请求,将请求分发给适当的处理器(Handler),并最终返回响应。它提供了共享算法用于处理请求,并将实际工作委托给其他组件完成。
- 配置方式
- 方式一:在 Web 应用中通过 Spring 配置文件(如 XML)进行初始化和声明
- 方式二:Java Config(如实现 WebApplicationInitializer 接口)进行初始化和声明
- 继承树:HttpServlet → HttpServletBean → FrameworkServlet → DispatcherServlet → GenericServlet
HandlerAdapter
-
定义:
-
功能:
-
核心方法
public interface HandlerAdapter {// 判断是否支持这个 handler(如是否是注解控制器)boolean supports(Object handler); // 真正执行业务逻辑,返回 ModelAndViewModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;// 用于缓存优化,一般用不到(服务端资源是否过期)long getLastModified(HttpServletRequest request, Object handler); }
-
常见实现类
实现类 适配的处理器类型 用途说明 RequestMappingHandlerAdapter
注解控制器(如 @Controller
,@RestController
)⭐ 最常用,现代 Spring 应用中主力 SimpleControllerHandlerAdapter
实现了 Controller
接口的类早期老项目可能使用 HttpRequestHandlerAdapter
实现 HttpRequestHandler
的类类似于 Servlet,处理低层请求 HandlerFunctionAdapter
Spring 5 的函数式风格 Handler WebFlux 或函数式注册 Handler 用
HandlerMapping
- 定义
- HandlerMapping:Spring MVC 中的一个接口,它定义了如何将 HTTP 请求 映射到对应的 controller 方法的规则
- ⭐ RequestMappingHandlerMapping :HandlerMapping 接口最重要的实现类,是默认启用的 HanderMapping,负责处理 @RequestMapping 相关的 Mapping
- RequestMappingHandlerMapping 功能
- 负责扫描和注册带有 @RequestMapping(以及其派生注解如 @GetMapping、@PostMapping 等)注解的处理器方法(Handler Methods)
- 负责将 @RequestMapping 注解的处理器方法与对应的请求路径(URL)映射起来,形成一个“请求映射表”
- 负责将请求 URL 映射到 @RequestMapping 注解的处理器方法
核心注解
注解 | 作用 |
---|---|
@ResponseBody | 表示方法的返回值直接写入 HTTP 响应体,而不是解析为视图 |
@RestController | 是 @Controller + @ResponseBody 的组合注解,类中所有方法默认都加上了 @ResponseBody |
@ResponseStatus | 指定 HTTP 响应状态码,不影响数据转换,但会影响响应状态 |
@ModelAttribute | 表示返回值会添加到模型(Model)中,用于视图渲染 |
@PathVariable / @RequestParam 等 | 用于方法参数绑定,与返回值无关 |
注意:如果不加 @RestController
也不加 @ResponseBody
,则返回的对象会被当做试图名称进行解析,不会触发**HttpMessageConverter
**
自定义参数解析过程
-
整体流程:“Adapter 找方法 → Invocable 找参数 → Resolver 解析值 → Binder 绑进对象”
DispatcherServlet#doDispatch↓ RequestMappingHandlerAdapter#invokeHandlerMethod↓ InvocableHandlerMethod#invokeForRequest↓ HandlerMethodArgumentResolverComposite#getMethodArgumentValues↓ ┌────────────── 内置 + 自定义 Resolver 逐个尝试 ──────────────┐RequestParamResolver │ PathVariableResolver │ RequestBodyResolver │ … │YourCustomResolver↓ └──────────────────────────────────────────────────────┘ WebDataBinder.bind() ← 针对 @ModelAttribute 或普通 JavaBean↓ Controller 方法真正执行
-
关键节点
步骤 关键类 / 方法 作用 源码片段 & 提示 ① 适配 RequestMappingHandlerAdapter#invokeHandlerMethod
创建 ServletWebRequest
、InvocableHandlerMethod
invocable.invokeAndHandle(webRequest, mavContainer);
(tty4.dev)② 解析参数 InvocableHandlerMethod#getMethodArgumentValues
遍历方法形参,委派给 参数解析器链 this.argumentResolvers.getArgumentResolver(parameter)
(github.com)③ 参数解析器 HandlerMethodArgumentResolverComposite
内置 + 自定义 Resolver 顺序匹配 自定义的 Resolver 会插在 内置之后( setCustomArgumentResolvers
)(docs.spring.io)④ 数据绑定 WebDataBinder.bind()
→BeanWrapperImpl.setPropertyValues()
把表单字段 / Query‑String 值 copy 到 JavaBean 字段,期间走 ConversionService 详见 ServletModelAttributeMethodProcessor
调createAttribute()
后调用binder.bind()
(baeldung.com)⑤ 类型转换 DefaultFormattingConversionService
String → Integer/Date/Enum
等全局共享,可通过 @InitBinder
或注册Converter
/Formatter
拓展
示例
-
场景:前端发送 JSON 请求,后端接收并转为 Java 对象
-
请求内容
- 请求头:
Content-Type: application/json
- 请求体:
{"name": "张三", "age": 25}
- 请求头:
-
Controller 方法
@PostMapping("/user") public ResponseEntity<String> createUser(@RequestBody User user) { // ... }
-
工作流程
@RequestBody
:让 Spring 知道需要把请求体转为 Java 对象。Content-Type: application/json
:让 Spring 从HttpMessageConverter
列表中找到支持 JSON 的转换器(比如 MappingJackson2HttpMessageConverter)。- Spring 使用该转换器将 JSON 字符串转成
User
对象。