Java面试(2025)—— Spring MVC
什么是Spring MVC
Spring MVC 是 Spring 框架的一个 基于 Java 的 Web 开发模块,它实现了 MVC(Model-View-Controller)架构模式,用于构建灵活、松耦合的 Web 应用程序。
它是 Spring 生态的核心组件之一,通过简化 HTTP 请求处理、数据绑定和视图渲染,帮助开发者高效构建企业级 Web 应用。
Spring MVC的优点
Spring MVC 是 Spring 生态中用于构建 Web 应用的核心模块,它的核心优势在于 模块化设计 和 注解驱动开发,能够高效处理 HTTP 请求并支持灵活的视图技术。我个人认为它的主要优点可以从以下几个方面展开:
(1) 松耦合的模块化设计
首先,Spring MVC 基于 前端控制器模式(DispatcherServlet
),将请求分发、业务处理、视图渲染等职责分离。
这种设计让各组件(如 Controller
、ViewResolver
)可以独立替换或扩展,比如从 JSP 切换到 Thymeleaf 只需修改配置,无需改动业务代码。
(2) 注解驱动的开发效率
其次,它通过 注解(如 @Controller
、@GetMapping
)极大简化了开发。对比传统的 Servlet 或 Struts2,代码量减少 50% 以上,且意图更清晰。
(3) 强大的数据绑定与验证
Spring MVC 支持 自动参数绑定(如 @RequestParam
、@ModelAttribute
)和 JSR-303 验证(如 @Valid
)。开发者无需手动解析 HTTP 参数,还能通过 BindingResult
统一处理校验错误,提升代码健壮性。
(4) 灵活的视图技术整合
它支持多种视图技术(JSP、Thymeleaf、Freemarker),并能无缝切换。对于前后端分离项目,还可以直接返回 JSON/XML(通过 @ResponseBody
),适应现代开发需求。
(5) 与 Spring 生态深度集成
作为 Spring 的一部分,它可以轻松整合其他组件,比如用 @Transactional
管理事务,或通过 Spring Security
实现权限控制。这种一致性降低了学习成本,也便于维护。
综上,Spring MVC 特别适合 需要快速开发 且 要求高可维护性 的项目,比如企业级后台管理系统或 RESTful API。它的灵活性和与 Spring 生态的无缝整合,是许多团队选择它的主要原因。
Spring MVC的主要组件
Spring MVC 主要组件
Spring MVC 框架由多个核心组件组成,它们协同工作来处理 HTTP 请求并生成响应。以下是主要组件及其功能:
DispatcherServlet (前端控制器)
- 核心控制器,负责接收所有请求
- 将请求委托给其他组件处理
- 继承自 HttpServlet
HandlerMapping (处理器映射器)
- 确定哪个 Controller 应该处理当前请求
- 默认实现:RequestMappingHandlerMapping
- 将 URL 映射到对应的处理方法
HandlerAdapter (处理器适配器)
- 实际调用处理器方法
- 处理不同类型的处理器
- 默认实现:RequestMappingHandlerAdapter
Controller (控制器)
- 处理业务逻辑
- 返回 ModelAndView 或直接响应
- 通常使用 @Controller 注解标记
ViewResolver (视图解析器)
- 解析逻辑视图名到实际视图
- 常见实现:InternalResourceViewResolver
- 支持多种视图技术(JSP, Thymeleaf等)
View (视图)
- 负责渲染模型数据
- 生成最终响应内容(HTML, JSON等)
HandlerExceptionResolver (异常处理器)
- 处理控制器执行过程中抛出的异常
- 可以自定义异常处理逻辑
MultipartResolver (文件上传解析器)
- 处理 multipart 文件上传请求
- 常见实现:CommonsMultipartResolver
什么是DispatcherServlet?
DispatcherServlet 是 Spring MVC 框架的核心组件,它充当了前端控制器的角色,负责协调整个请求处理流程。
主要职责
- 请求接收:作为 Servlet,它接收所有的 HTTP 请求
- 请求分发:将请求委托给适当的控制器进行处理
- 视图渲染:协调视图解析和渲染过程
- 异常处理:处理请求处理过程中出现的异常
- 结果返回:将最终响应返回给客户端
工作原理
- 接收客户端请求
- 查询 HandlerMapping 确定处理请求的 Controller
- 通过 HandlerAdapter 调用实际的 Controller 方法
- 处理 Controller 返回的 ModelAndView 或直接响应
- 通过 ViewResolver 解析视图名称
- 渲染视图并返回响应
什么是Spring MVC框架的控制器?
简明定义(先给结论)
Spring MVC控制器是处理客户端请求并返回响应的核心组件,它负责接收用户请求、协调业务逻辑处理,并决定如何响应客户端。
核心特性(展示深度)
可以补充以下关键点:
- 注解驱动:主要通过
@Controller
和@RestController
注解标识 - 请求映射:使用
@RequestMapping
及其变体(@GetMapping
等)定义URL映射 - 多返回值支持:可以返回视图名称、ModelAndView、响应体或ResponseEntity
- 松散耦合:与视图技术解耦,支持JSP、Thymeleaf等多种视图
- REST支持:通过
@RestController
简化RESTful API开发
工作流程(结合架构)
在Spring MVC架构中,控制器位于DispatcherServlet之后:
- DispatcherServlet接收到请求后
- 通过HandlerMapping找到匹配的控制器方法
- 方法执行后返回处理结果
- 结果经过视图解析器或消息转换器处理
- 最终生成响应返回客户端
Spring MVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?
Spring MVC中的控制器默认是单例的(Singleton),这是由Spring容器的默认作用域决定的。在Spring中,所有Bean默认都是单例的,包括使用@Controller
和@RestController
注解的类。
单例模式带来的潜在问题
- 线程安全问题
- 多个HTTP请求会并发访问同一个控制器实例
- 如果控制器有实例变量(成员变量),这些变量会被所有请求共享
- 可能导致数据混乱或并发修改异常
- 状态保持问题
- 单例控制器不适合保存请求特定的状态
- 例如:在成员变量中存储请求相关的数据会导致不同请求间的数据污染。
解决方案
1. 避免使用实例变量(推荐)
最佳实践是控制器中不定义任何实例变量,所有请求特定的数据都应通过方法参数传递。
2. 使用方法局部变量
所有请求特定的数据都应在方法内部创建:
请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?
时序图
注意:
- HandlerMapping返回的是Hander或者是处理器链(处理器+拦截器);
- 拦截器执行的三个时机:
(1)适配器调用前执行 (preHandle
)
- 时机:在 HandlerAdapter 调用控制器方法之前
- 作用:权限校验、日志记录、参数预处理等
- 中断能力:若任一拦截器的
preHandle
返回false
,后续拦截器和控制器方法均不会执行 - 顺序:按拦截器配置的正序执行
(2)适配器调用后执行 (postHandle
)
- 时机:在 HandlerAdapter 调用控制器方法之后,但在视图渲染之前
- 作用:修改模型数据、记录执行结果等
- 注意:此时控制器已执行完毕,但响应未写入客户端
- 顺序:按拦截器配置的逆序执行;
(3)请求完成后执行 (afterCompletion
)
- 时机:在视图渲染完成后(整个请求处理完毕时)
- 作用:资源清理、性能监控统计等
- 特点:无论请求成功或异常都会执行
- 顺序:按拦截器配置的逆序执行
过滤器和拦截器
特性 | 过滤器(Filter) | 拦截器(Interceptor) |
所属规范 | Servlet 规范 | Spring MVC 机制 |
执行位置 | DispatcherServlet 前 | DispatcherServlet 后 |
依赖 | 依赖 Servlet 容器 | 依赖 Spring 容器 |
可访问对象 | ServletRequest/Response | 可以获取 HandlerMethod 等Spring对象 |
使用场景 | 底层请求/响应处理 | 业务相关的横切关注点 |
Spring MVC常用的注解有哪些?
Spring MVC 提供了丰富的注解来简化 Web 开发,以下是常用注解分类整理:
请求映射相关
注解 | 作用 | 示例 |
| 通用请求映射(可指定HTTP方法、头等) |
|
| 简化GET请求映射 |
|
| 简化POST请求映射 |
|
| 简化PUT请求映射 |
|
| 简化DELETE请求映射 |
|
| 简化PATCH请求映射 |
|
参数处理
注解 | 作用 | 示例 |
| 获取URL查询参数或表单数据 |
|
| 获取URI模板变量 |
|
| 接收JSON/XML请求体 |
|
| 获取请求头值 |
|
| 获取Cookie值 |
|
| 绑定参数到模型对象 |
|
响应处理
注解 | 作用 | 示例 |
| 将返回值直接写入HTTP响应体 |
|
| 自定义HTTP响应状态码 |
|
|
|
|
异常处理
注解 | 作用 | 示例 |
| 处理控制器内的异常 |
|
| 全局异常处理(配合 |
|
数据验证
注解 | 作用 | 示例 |
| 触发JSR-303验证 |
|
| Spring的验证注解(支持分组验证) |
|
| 字段非空校验(需配合 |
|
视图与模型
注解 | 作用 | 示例 |
| 定义控制器类 |
|
| 模型属性存入Session |
|
| 解决跨域问题 |
|
其他实用注解
注解 | 作用 | 示例 |
| 自定义数据绑定逻辑 |
|
| 日期格式转换 |
|
完整代码示例
关键说明:
- 组合注解:如
@RestController
是@Controller
+@ResponseBody
的组合 - 参数绑定:
@RequestParam
默认必传,可通过required=false
改为可选 - 验证联动:
@Valid
需与JSR-303注解(如@NotNull
)配合使用 - RESTful设计:推荐使用
@GetMapping
/@PostMapping
等简化注解
掌握这些注解可以覆盖90%的Spring MVC开发场景!
@Controller注解的作用
@Controller
是 Spring MVC 中最核心的注解之一,用于标记一个类作为控制器(Controller),专门处理 HTTP 请求并返回响应。以下是它的详细作用解析:
- 标识控制器类
告诉 Spring 容器:当前类是一个 MVC 控制器,需要被扫描并管理其生命周期。 - 接收 HTTP 请求
与@RequestMapping
等注解配合,定义处理特定 URL 请求的方法。 - 协调模型(Model)和视图(View)
在传统 MVC 模式中,控制器负责处理业务逻辑,返回视图名称(如 JSP/Thymeleaf),由视图解析器渲染。
@RequestMapping注解的作用
@RequestMapping
是 Spring MVC 中最核心的注解之一,用于将 HTTP 请求映射到特定的控制器方法或类上。它提供了灵活的 URL 匹配规则,支持定义请求方法、参数、头部等条件。以下是详细解析:
- URL 路径映射
将 HTTP 请求的 URL 路径与控制器方法绑定,支持:
- 精确路径(如
/users
) - 路径变量(如
/users/{id}
) - 通配符(如
/resources/**
)
- HTTP 方法约束
指定处理的请求类型(GET/POST/PUT/DELETE 等)。 - 请求参数/头部过滤
通过附加条件(如参数必须存在、头部匹配)进一步筛选请求。
@ResponseBody注解的作用
@ResponseBody
是 Spring MVC 中用于将方法返回值直接写入 HTTP 响应体的核心注解,通常用于构建 RESTful API 或处理 AJAX 请求。以下是它的详细解析:
- 跳过视图渲染
不再通过视图解析器(如 JSP/Thymeleaf)渲染,而是直接将返回值序列化为 JSON/XML 等格式写入响应体。 - 自动内容协商
根据请求的Accept
头或 URL 后缀(如.json
),自动选择合适的HttpMessageConverter
进行序列化。 - 支持多种返回类型
可返回 Java 对象、集合、字符串等,Spring 会自动处理序列化。
@PathVariable和@RequestParam的区别
@PathVariable
和 @RequestParam
是 Spring MVC 中用于处理 HTTP 请求参数的两种核心注解,它们的主要区别体现在 参数来源、使用场景 和 URL 风格 上。以下是详细对比:
核心区别总结
特性 |
|
|
参数来源 | 从 URL 路径 中提取 | 从 URL 查询字符串 或 表单数据 中提取 |
URL 示例 |
|
|
是否必传 | 默认必传(除非指定默认值) | 可通过 设为可选 |
RESTful 风格 | 是(推荐用于资源标识) | 否(传统查询方式) |
数据类型转换 | 自动(Spring 类型转换机制) | 自动(Spring 类型转换机制) |
使用场景对比
1. @PathVariable
(路径变量)
- 适用场景:标识 RESTful 资源唯一性(如 ID、用户名等)
- 示例代码:
- URL 风格:
2. @RequestParam
(请求参数)
- 适用场景:过滤、排序、分页等非核心参数
- 示例代码:
- URL 风格: