本文并非简单的资料堆砌,而是结合笔者多年的开发与面试经验,对海量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):
DispatcherServlet
consulta allHandlerMapping
beans 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):
DispatcherServlet
usesHandlerAdapter
to actually execute the Handler. The adapter acts as a bridge, allowingDispatcherServlet
to invoke a wide variety of handlers (e.g.,@Controller
annotated methods,HttpRequestHandler
) in a uniform way. - 业务处理(Handler/Controller):The target Controller method is invoked. It performs business logic, interacts with services, and returns a
ModelAndView
object (or just aView
name, or even raw data if annotated with@ResponseBody
). - 视图解析(ViewResolver):
DispatcherServlet
receives the logical view name from theModelAndView
and asks theViewResolver
to resolve it into a specificView
object (e.g., JstlView, ThymeleafView). - 视图渲染(View):
DispatcherServlet
passes the model data to theView
object. TheView
renders 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
DispatcherServlet
to 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=20
public 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)进行对比,才能在面试中展现出你的核心竞争力。祝你成功!