Spring Web MVC是Spring家族中最早出现的模块之一,也是目前Java Web开发中使用最广泛的框架。简单来说,它是一个基于Servlet构建的Web层框架,围绕请求分发、参数绑定、视图渲染这三件事,把过去需要手写大量配置的Servlet开发模式,简化成几个注解加一个Controller类的工作方式。很多初学者虽然天天在用@Controller和@RequestMapping,但对请求到底是怎么流转的、框架在背后做了哪些事,往往一知半解,结果遇到问题就只能靠猜。这篇文章就把Spring Web MVC的定位、核心原理和常见误区一次讲透。

Spring Web MVC到底是什么,它解决了什么问题
在没有MVC框架的年代,用纯Servlet写Web应用的体验相当糟糕。你需要自己继承HttpServlet,重写doGet和doPost方法,手动解析请求参数、做类型转换、处理中文乱码、拼接跳转路径,每加一个功能就要多写一个Servlet,web.xml文件也越写越长。代码里充斥着与业务无关的重复劳动,可维护性很差。
Spring Web MVC的核心价值就是把这套流程标准化了。它引入了前端控制器模式,由一个总入口统一接收所有请求,再根据配置把请求分发给对应的处理器。参数解析、类型转换、视图定位这些通用工作全部由框架承担,开发者只需要关注业务逻辑本身。写一个接口从原来的几十行Servlet代码,变成了如下几行:
@Controller
public class UserController {
@RequestMapping(value = "/user/detail", method = RequestMethod.GET)
@ResponseBody
public User getUserDetail(@RequestParam("id") Long id) {
// 框架自动完成参数提取和Long类型转换,这里直接写业务逻辑
return userService.findById(id);
}
}从职责划分上看,MVC三个字母分别对应Model(数据模型)、View(视图)、Controller(控制器)。Spring Web MVC在这套结构里主要承担Controller层和View层的调度工作,Model则通常配合Service层和ORM框架来完成。值得一提的是,现在前后端分离已经成为主流,View这个环节大多被JSON序列化取代,但框架的整体架构并没有变,只是视图渲染这一步换成了消息转换器。
核心原理:DispatcherServlet的一次完整请求流程
要理解Spring Web MVC,关键是搞懂DispatcherServlet这个总调度器。它在框架中的地位类似于一个总前台,所有进入应用的请求都会先经过它。可以在web.xml中看到这样的配置:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/<url-pattern>
</servlet-mapping>一个请求进来之后,DispatcherServlet大致会走这样一条链路:先通过HandlerMapping找到能处理这个请求的Handler(也就是Controller里的某个方法),然后通过HandlerAdapter执行这个Handler,执行前会经过一系列HandlerInterceptor的preHandle方法。Handler执行完毕返回ModelAndView(或直接返回对象由消息转换器处理),再经过拦截器的postHandle,最后完成视图渲染或响应写入,收尾时执行afterCompletion。
这套流程里有几个组件值得单独认识一下。HandlerMapping负责建立请求地址与处理方法之间的映射关系,常用的RequestMappingHandlerMapping会扫描所有带@RequestMapping注解的方法并生成映射表。HandlerAdapter负责真正的调用执行,之所以要有一层适配器,是因为Handler的实现方式可能多种多样,适配器模式让DispatcherServlet不必关心具体细节。HttpMessageConverter则负责请求体和响应体的序列化与反序列化,这就是为什么我们在方法上加一个@ResponseBody,返回的对象就能自动变成JSON。理解了这几个组件的分工,排查问题时就有了方向,比如接口返回404,多半是HandlerMapping没有找到映射;参数绑定失败,则要去查对应的ArgumentResolver。
常用注解的正确打开方式
注解驱动是Spring Web MVC开发的主要风格。@Controller标记一个类为控制器,@RequestMapping用于建立URL映射,@GetMapping和@PostMapping是其简化形式,分别限定请求方式。接收参数时,@RequestParam用于获取查询参数或表单字段,@PathVariable用于获取路径中的占位符,@RequestBody用于把请求体反序列化成Java对象,三者职责完全不同,不能混用。
@RestController
@RequestMapping("/order")
public class OrderController {
// GET /order/list?pageNum=1 page参数没传时使用默认值1
@GetMapping("/list")
public List<Order> list(@RequestParam(defaultValue = "1") int pageNum) {
return orderService.findByPage(pageNum);
}
// GET /order/10086 id直接从路径中取
@GetMapping("/{id}")
public Order detail(@PathVariable("id") Long id) {
return orderService.findById(id);
}
// POST /order/create 请求体为JSON字符串
@PostMapping("/create")
public String create(@RequestBody Order order) {
orderService.save(order);
return "ok";
}
}@RestController等于@Controller加@ResponseBody,用了它之后类中所有方法的返回值都会走消息转换器而不是视图解析。此外@RequestHeader取请求头,@CookieValue取Cookie,这些注解都遵循同一个思路:框架根据注解类型选择对应的ArgumentResolver来完成参数装配,开发者不需要碰HttpServletRequest就能拿到想要的数据,代码可读性和可测试性都好了很多。
初学者最容易踩的几个坑
第一个坑是把@PathVariable和@RequestParam搞混。路径占位符必须用@PathVariable接收,query参数必须用@RequestParam接收,用错了会直接报参数缺失异常。还有一种情况是参数名在编译时被压缩,某些场景下注解不写参数名就会绑定失败,所以建议养成显式写明参数名的习惯,比如@RequestParam("id"),这样最稳妥。
第二个坑是对404和静态资源拦截的误解。如果DispatcherServlet的url-pattern配成了/,它会拦截所有请求但不包括JSP,这时访问html、js、图片等静态资源可能404,需要在配置中启用默认Servlet或配置资源映射。另外要注意/和/*的区别,后者会连JSP一起拦截,容易导致页面渲染异常,这是纯配置层面的经典错误。
第三个坑是拦截器与过滤器的顺序问题。Filter属于Servlet规范,在DispatcherServlet之前执行;Interceptor属于Spring MVC,在DispatcherServlet内部执行。完整顺序是Filter的前置处理、Interceptor的preHandle、Controller执行、Interceptor的postHandle、视图渲染、Interceptor的afterCompletion、Filter的后置处理。跨域、编码这类与业务无关的处理适合放Filter,登录校验、权限控制这类需要Spring上下文的逻辑适合放Interceptor,别把职责放反了。
第四个坑是JSON序列化中的日期和循环引用问题。默认的Jackson对日期类型的格式化结果往往不符合预期,建议通过配置统一指定格式,同时在实体之间存在双向引用时要加@JsonIgnore避免无限递归。这类问题在联调阶段最容易被发现,提前了解能省下不少排查时间。掌握这些细节之后,再回头看待Spring Web MVC,你会发现它并非黑盒,而是一个职责清晰、可以逐层理解的请求处理管道,用好它的前提是理解它的每一环。
Spring Web MVCDispatcherServletSpringMVC注解修改时间:2026-09-08 21:23:26