本文并非简单的资料堆砌,而是结合笔者多年的开发与面试经验,对海量SpringMVC知识点进行系统化梳理、深度整合与扩展而成。旨在为你提供一份视角独特、内容充实、重点突出的面试备战指南,助你在2025年的技术面试中所向披靡。
文章目录
阅读指南
- 初学者:建议按顺序阅读,从“核心要义”建立宏观概念,再深入“工作机制”与“核心组件”,最后攻克“高级特性”。
- 面试突击者:可直接通过目录定位到你最薄弱的环节进行重点强化。尤其不要错过“深度对比”和“实战剖析”章节。
- 追求深度者:“源码视角”和“扩展思考”章节将带你触及框架设计思想,让你在面试中脱颖而出。
第一章:SpringMVC核心要义与工作探秘
1.1 什么是Spring MVC?谈谈你的理解(⭐⭐⭐)
参考答案:
Spring MVC是Spring Framework生态系统中的一个模块,它是一个基于Java实现的、轻量级的、请求驱动的Web框架,其核心设计思想是MVC(Model-View-Controller)架构模式。
我的理解是,它的价值在于:
- 职责解耦:通过将应用清晰地划分为模型(数据处理)、视图(界面展示)、控制器(请求调度)三层,使得代码结构清晰,各司其职,极大提升了代码的可维护性和可扩展性。
- 与Spring无缝集成:它可以完美地利用Spring核心容器的IOC(控制反转) 和AOP(面向切面编程) 等强大功能,方便地进行依赖注入和声明式事务管理等。
- 高度可配置与可扩展:从处理器映射、视图解析到数据绑定,几乎所有组件都是可插拔的,开发者可以根据需求进行定制和扩展。
- 强大的注解驱动:通过
@Controller,@RequestMapping等注解,极大地简化了配置,使开发变得非常简洁和高效。 - 支持RESTful风格:天然支持构建RESTful API,是现代Web应用和微服务开发的理想选择。
面试官视角:此问题考察对框架的宏观认知。不要只背诵定义,要结合设计模式、框架优势、实际应用来谈,展现你的深度思考。
1.2 详述SpringMVC的工作流程(⭐⭐⭐⭐⭐)
这是必考题,请务必理解并能清晰地描述。其核心流程围绕DispatcherServlet展开,可概括为以下步骤:
- 用户请求达:HTTP请求到达Web容器(如Tomcat),匹配
web.xml或Servlet规范中配置的DispatcherServlet的映射路径。 - 中央调度(DispatcherServlet):作为“前端控制器”,它是整个流程的调度中心,接收请求后并不处理具体业务,而是协调后续组件。
- 寻找处理器(HandlerMapping):
DispatcherServletconsulta allHandlerMappingbeans to find out whichController(Handler) can handle this request based on the request URL. It returns not only the Handler but also aHandlerExecutionChain(which may include interceptors). - 执行适配(HandlerAdapter):
DispatcherServletusesHandlerAdapterto actually execute the Handler. The adapter acts as a bridge, allowingDispatcherServletto invoke a wide variety of handlers (e.g.,@Controllerannotated methods,HttpRequestHandler) in a uniform way. - 业务处理(Handler/Controller):The target Controller method is invoked. It performs business logic, interacts with services, and returns a
ModelAndViewobject (or just aViewname, or even raw data if annotated with@ResponseBody). - 视图解析(ViewResolver):
DispatcherServletreceives the logical view name from theModelAndViewand asks theViewResolverto resolve it into a specificViewobject (e.g., JstlView, ThymeleafView). - 视图渲染(View):
DispatcherServletpasses the model data to theViewobject. TheViewrenders itself, combining the model data with the template (e.g., JSP, HTML), and generates the final output (e.g., HTML). - 响应返回:The rendered response is sent back through
DispatcherServletto the client’s browser.
面试官视角:考察对框架核心架构的理解。光背步骤不行,最好能说出每个核心组件(DispatcherServlet, HandlerMapping, HandlerAdapter, ViewResolver)的职责。
第二章:核心组件与注解驱动开发
2.1 SpringMVC的核心组件有哪些?各自承担什么角色?(⭐⭐⭐⭐)
| 组件名称 | 角色定位 | 核心职责 |
|---|---|---|
DispatcherServlet | 前端控制器,调度中心 | 统一接收请求,分配任务,返回响应,降低组件间耦合度 |
HandlerMapping | 处理器映射器 | 根据请求的URL,找到对应的处理器(Handler/Controller) |
HandlerAdapter | 处理器适配器 | 以统一的适配接口调用各种不同类型的处理器(如基于注解的、基于接口的) |
Handler | 处理器(通常叫Controller) | 程序员开发,执行具体的业务逻辑,处理请求 |
ViewResolver | 视图解析器 | 将控制器返回的逻辑视图名解析为具体的视图对象(如JSP页面) |
View | 视图 | 负责将模型数据渲染成最终的展示形式(HTML/JSON/XML等) |
HandlerInterceptor | 拦截器 | 对请求进行预处理和后处理,实现权限校验、日志记录等通用功能 |
2.2 SpringMVC常用注解有哪些?(⭐⭐⭐)
| 注解 | 应用层级 | 作用 |
|---|---|---|
@Controller | 类 | 标记一个类为SpringMVC的控制器,允许被组件扫描 |
@RestController | 类 | @Controller + @ResponseBody,专用于RESTful API,直接返回数据而非视图 |
@RequestMapping | 类/方法 | 映射WEB请求(URL)到特定的处理器类或方法上,可窄化路径 |
@GetMapping/@PostMapping等 | 方法 | @RequestMapping的组合注解,限定特定的HTTP方法,代码更简洁 |
@RequestParam | 方法参数 | 将请求参数绑定到控制器的方法参数上 |
@PathVariable | 方法参数 | 将URL中的模板变量绑定到方法参数上(RESTful风格) |
@RequestBody | 方法参数 | 将请求体中的JSON/XML数据解析并绑定到Java对象上 |
@ResponseBody | 方法 | 将方法返回的对象,通过HttpMessageConverter转换为指定格式(JSON/XML)写入响应体 |
@ModelAttribute | 方法/参数 | 将参数或方法返回值绑定到模型(Model)中,供视图使用 |
@SessionAttributes | 类 | 将模型中的属性透明地存储到HttpSession中 |
@ControllerAdvice/@ExceptionHandler | 类/方法 | 全局异常处理,用于统一处理控制器抛出的异常 |
2.3 @RequestMapping 和 @RestController 的区别?
@RequestMapping:通常与@Controller搭配使用。方法返回值通常是一个视图名称(String),由ViewResolver解析为物理视图进行渲染。如果希望方法返回数据,需要在方法上额外添加@ResponseBody。@RestController:是@Controller和@ResponseBody的组合注解。意味着该类中所有方法的返回值都会直接写入响应体,而不是被当作视图名解析。它是开发RESTful Web服务的首选注解。
第三章:请求处理与数据响应
3.1 如何接收前端传来的各种参数?
- 普通参数:
/user?name=John&age=20public String method(@RequestParam("name") String username, @RequestParam(value = "age", required = false, defaultValue = "18") int age) {} - 路径参数(RESTful):
/user/1@GetMapping("/user/{id}") public String method(@PathVariable("id") Long userId) {} - POJO对象:参数名与对象的属性名匹配,SpringMVC会自动封装。
// POST请求体: name=John&age=20 public String method(User user) {} // 自动new User()并setName、setAge - JSON数据:
@PostMapping("/user") @ResponseBody public User createUser(@RequestBody User user) { // 将请求体中的JSON解析为User对象 return userService.save(user); } - 原生API:直接在方法参数中声明
HttpServletRequest,HttpServletResponse,HttpSession等。
3.2 如何向页面传递数据?
- Model/ModelMap/Map:数据默认放在Request域中。
public String method(Model model) { model.addAttribute("key", value); return "view-name"; } - ModelAndView:合并了模型数据和视图信息。
public ModelAndView method() { ModelAndView mav = new ModelAndView("view-name"); mav.addObject("key", value); return mav; } - @ModelAttribute:将数据自动放入模型。
- @SessionAttributes:配合
@Controller使用,将指定的Model数据提升到Session域中,用于跨请求数据保持。@Controller @SessionAttributes("userInfo") // 将model中名为"userInfo"的属性存入session public class MyController { @PostMapping("/login") public String login(Model model, User user) { // ... validate user model.addAttribute("userInfo", user); // 这个userInfo会自动存入session return "redirect:/home"; } }
3.3 如何进行重定向和转发?
- 转发(Forward):服务器内部跳转,地址栏URL不变。
return "forward:/path/to/target"; // 转发到一个URL return "view-name"; // 默认就是转发到视图解析器解析的页面 - 重定向(Redirect):客户端重新发起请求,地址栏URL改变。
注意:重定向传递参数需通过URL参数(如return "redirect:/path/to/target";redirect:/user?id=1)或Flash属性(RedirectAttributes.addFlashAttribute())。
第四章:高级特性与实战剖析
4.1 如何配置和使用拦截器(Interceptor)?(⭐⭐⭐)
拦截器用于实现横切关注点(如日志、权限、国际化)。
- 实现拦截器:实现
HandlerInterceptor接口,主要重写preHandle(请求前)、postHandle(处理后)、afterCompletion(完成后)三个方法。public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 权限检查逻辑 HttpSession session = request.getSession(); User user = (User) session.getAttribute("currentUser"); if (user == null) { response.sendRedirect("/login"); return false; // 中断执行链 } return true; // 放行 } // ... 其他方法 } - 注册拦截器:通过配置类(
WebMvcConfigurer)注册并指定拦截路径。@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns("/**") // 拦截所有路径 .excludePathPatterns("/login", "/css/**", "/js/**"); // 排除路径 } }
4.2 SpringMVC的异常处理机制?(⭐⭐⭐)
- 局部处理:在Controller内部使用
@ExceptionHandler注解,只能处理本Controller内的异常。 - 全局处理(推荐):使用
@ControllerAdvice(或@RestControllerAdvice)搭配@ExceptionHandler,定义一个全局异常处理类,处理所有控制器抛出的异常。@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<String> handleAllException(Exception ex) { // 记录日志 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Server Error: " + ex.getMessage()); } @ExceptionHandler(UserNotFoundException.class) public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); } }
4.3 SpringMVC是单例模式吗?线程安全如何保证?(⭐⭐⭐)
- 是单例的:默认情况下,Spring容器中的Controller、Service等都是单例的。
- 线程安全问题:单例意味着所有线程共享一个实例实例变量(成员变量),如果存在可变的成员变量,则会有线程安全问题。
- 解决方案:
- 最佳实践:不要在Controller中定义成员变量。所有需要的数据都通过方法参数(
HttpRequest,Model等)传递或从Service层获取。 - 如果必须要有状态(极少情况),可以使用
ThreadLocal或者通过注解@Scope(value = "prototype")将Bean的作用域改为原型(每次请求创建新实例),但后者会增加GC负担,一般不推荐。
- 最佳实践:不要在Controller中定义成员变量。所有需要的数据都通过方法参数(
第五章:深度对比与扩展思考
5.1 SpringMVC 与 Struts2 的核心区别?(⭐⭐)
| 方面 | SpringMVC | Struts2 |
|---|---|---|
| 入口 | Servlet (DispatcherServlet) | Filter (StrutsPrepareAndExecuteFilter) |
| 设计理念 | 方法级拦截,更轻量 | 类级拦截,一个Action实例处理一个请求 |
| 性能 | 更高,无需为每次请求创建新控制器 | 稍低,通常为每次请求创建一个Action实例 |
| 参数注入 | 通过方法参数灵活绑定,支持多种注解 | 通过Setter方法注入到对象属性中 |
| 与Spring集成 | 无缝集成,是亲儿子 | 需要额外整合 |
| 社区与趋势 | 主流,社区活跃,是未来方向 | 逐渐式微,新项目很少采用 |
5.2 中文乱码问题如何解决?
- POST请求乱码:在
web.xml中配置Spring提供的CharacterEncodingFilter,并设置encoding为UTF-8,且forceEncoding为true。<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> - GET请求乱码:修改Tomcat服务器的
server.xml配置文件,在<Connector>标签中增加URIEncoding="UTF-8"属性。
5.3 谈谈你对HandlerAdapter的理解
这是一个考察深度的好问题。HandlerAdapter是适配器模式的典型应用。
因为SpringMVC支持的处理器类型多种多样(如@Controller注解方式、实现Controller接口的方式、HttpRequestHandler方式等),它们的执行方式各不相同。DispatcherServlet如果直接调用,会产生沉重的if-else判断和强耦合。
HandlerAdapter的出现,让DispatcherServlet只需与统一的HandlerAdapter接口交互,由具体的Adapter实现去调用目标Handler。这极大地提高了框架的扩展性,如果需要支持一种新的处理器类型,只需新增一个HandlerAdapter实现即可,无需修改DispatcherServlet的代码。
第六章:源码视角与性能优化
(本章节为拔高内容,适用于对原理有深入追求的面试者)
6.1 DispatcherServlet 的 doDispatch 方法概要
这是整个MVC流程的核心方法,其伪代码逻辑清晰地展示了我们第一章描述的流程:
- 检查是否为文件上传请求。
- 根据
HandlerMapping获取HandlerExecutionChain(包含Handler和Interceptors)。 - 根据Handler获取对应的
HandlerAdapter。 - 按顺序应用拦截器的
preHandle方法。 - 使用
HandlerAdapter实际调用处理器的处理方法(ha.handle()),返回ModelAndView。 - 应用拦截器的
postHandle方法。 - 处理分发结果(渲染视图
processDispatchResult)。 - 触发拦截器的
afterCompletion方法(无论成功与否)。
6.2 性能优化建议
- 控制器保持无状态:避免线程安全问题,减少同步开销。
- 合理使用拦截器:拦截路径要精确,避免拦截静态资源。
- 视图解析优化:对于稳定的视图名,可以考虑缓存解析结果(某些
ViewResolver支持)。 - JSON序列化优化:选择高性能的JSON库(如Jackson afterburner模块、Fastjson)并合理配置。
- 异步处理:对于耗时操作,使用SpringMVC的异步处理(
Callable、DeferredResult)来释放容器线程,提高吞吐量。
如需获取更多实战面试题宝典(Spring/MySQL/Redis/MongoDB/Elasticsearch/Kafka等),请持续关注本专栏《面试题宝典》系列文章。
总结:掌握SpringMVC,不仅要知其然,更要知其所以然。理解其设计思想(如前端控制器模式、适配器模式)、核心流程和常用注解是基础。在此基础上,深入拦截器、异常处理、全局配置等高级特性,并能与其它技术(如Struts2)进行对比,才能在面试中展现出你的核心竞争力。祝你成功!
本文并非简单的资料堆砌,而是结合笔者多年的开发与面试经验,对海量SpringMVC知识点进行系统化梳理、深度整合与扩展而成。旨在为你提供一份视角独特、内容充实、重点突出的面试备战指南,助你在2025年的技术面试中所向披靡。
7303





