本文收录于「Java 学习日记」专栏,聚焦 SpringMVC 的核心骨架 ------DispatcherServlet 前端控制器与请求映射机制,从底层流程拆解到注解式实战,帮你打通 SpringMVC 入门的关键一步~
一、为什么要学 SpringMVC?
在前序的 Spring 核心学习中,我们掌握了 IOC/DI、AOP 等基础,但在 Web 开发场景下,还面临这些问题:
- 手动编写 Servlet 需要处理请求分发、参数解析、视图跳转等重复逻辑,开发效率低;
- 传统 MVC 手动实现的框架功能简陋,无法适配复杂的 Web 场景(如 RESTful 接口、文件上传);
- 无法与 Spring 核心容器无缝整合,依赖注入、AOP 等特性无法在 Web 层复用。
而SpringMVC作为 Spring 框架的 Web 模块,完美解决了这些问题:
- 基于 MVC 设计模式,封装了 Servlet 底层细节,提供简洁的注解式开发;
- 核心组件 DispatcherServlet 统一处理所有请求,实现请求分发、参数绑定、视图解析等自动化;
- 与 Spring 核心容器无缝整合,可直接复用 IOC/DI、AOP 等特性;
- 支持 RESTful API、文件上传、拦截器、异常处理等企业级特性,是 Java Web 开发的主流框架。
今天这篇日记,我们从 SpringMVC 核心流程入手,聚焦 DispatcherServlet 和请求映射两大核心,通过实战掌握 SpringMVC 的入门用法。
二、SpringMVC 核心架构与工作流程
1. SpringMVC 核心组件
SpringMVC 的核心是 "前端控制器 + 组件化处理",核心组件分工如下:
| 组件 | 中文名称 | 核心职责 |
|---|---|---|
| DispatcherServlet | 前端控制器 | 接收所有请求,分发到其他组件,核心调度者 |
| HandlerMapping | 处理器映射器 | 根据请求路径匹配对应的 Controller 方法 |
| HandlerAdapter | 处理器适配器 | 调用 Controller 方法,处理参数绑定、返回值解析 |
| Controller/Handler | 处理器(控制器) | 处理请求逻辑,返回视图名 / 数据 |
| ViewResolver | 视图解析器 | 将视图名解析为具体的视图对象(如 JSP/HTML) |
| View | 视图 | 渲染页面,返回响应给客户端 |
| HandlerInterceptor | 拦截器 | 对请求进行前置 / 后置处理(如登录校验) |
2. SpringMVC 完整工作流程(核心)
用流程图直观展示请求从客户端到服务端的全流程:

3. 核心流程拆解(通俗版)
- 请求进门:所有请求都先到 DispatcherServlet(相当于公司前台);
- 找对应员工:HandlerMapping 根据请求 URL 找到对应的 Controller 方法(前台根据需求找对应业务员工);
- 适配调用:HandlerAdapter 负责调用 Controller 方法,处理参数、返回值等细节(行政帮员工准备好工作条件);
- 处理业务:Controller 处理具体业务逻辑,返回视图名(员工处理具体工作,告诉前台结果);
- 找视图页面:ViewResolver 根据视图名找到具体的页面(前台根据结果找对应的输出模板);
- 返回响应:View 渲染页面并返回给客户端(输出最终结果)。
三、SpringMVC 入门实战:环境搭建与核心配置
1. 开发环境准备
- JDK:8 及以上;
- 构建工具:Maven;
- Web 容器:Tomcat 8.5+;
- SpringMVC 版本:5.3.x(与 Spring 核心版本一致)。
2. Maven 依赖配置(核心)
xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>springmvc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>5.3.29</spring.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- SpringMVC核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Servlet API(Tomcat已提供,编译时依赖) -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSP API(Tomcat已提供) -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!-- JSTL(JSP标签库) -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<!-- 构建配置:指定war包名称、编译插件 -->
<build>
<finalName>springmvc-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<!-- Tomcat插件(方便运行) -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
3. 配置 DispatcherServlet(web.xml)
DispatcherServlet 是 SpringMVC 的核心,需要在web.xml中配置(相当于注册 "前台"):
xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置SpringMVC前端控制器DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定SpringMVC配置文件路径(默认/WEB-INF/[servlet-name]-servlet.xml) -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-config.xml</param-value>
</init-param>
<!-- 启动时加载(优先级1),Tomcat启动时就初始化DispatcherServlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 映射所有请求到DispatcherServlet -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- /:匹配所有请求(除了JSP),/*:匹配所有请求(包括JSP,不推荐) -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器(解决POST请求中文乱码) -->
<filter>
<filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
4. SpringMVC 核心配置文件(springmvc-config.xml)
xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 1. 扫描Controller包下的注解(@Controller/@RequestMapping等) -->
<context:component-scan base-package="com.example.controller"/>
<!-- 2. 开启SpringMVC注解驱动(支持@RequestMapping、参数绑定等) -->
<mvc:annotation-driven/>
<!-- 3. 配置视图解析器(将视图名解析为JSP路径) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:JSP文件所在目录 -->
<property name="prefix" value="/WEB-INF/views/"/>
<!-- 后缀:JSP文件后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 4. 静态资源放行(如CSS/JS/图片,否则DispatcherServlet会拦截) -->
<mvc:default-servlet-handler/>
</beans>
四、请求映射实战:@RequestMapping 注解
1. 核心注解说明
@RequestMapping是 SpringMVC 中最核心的请求映射注解,用于将请求 URL 绑定到 Controller 方法,支持类级和方法级注解:
| 注解属性 | 作用 | 示例 |
|---|---|---|
| value/path | 指定请求 URL 路径 | @RequestMapping("/user/query") |
| method | 指定请求方式(GET/POST/PUT/DELETE) | method = RequestMethod.GET |
| params | 指定请求必须包含的参数 | params = "id"(必须有 id 参数) |
| headers | 指定请求必须包含的请求头 | headers = "Content-Type=application/json" |
| produces | 指定响应内容类型 | produces = "application/json" |
2. 编写 Controller(核心)
java
运行
java
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
// 标记为SpringMVC控制器(由SpringMVC容器管理)
@Controller
// 类级请求映射:所有方法的URL前缀为/user
@RequestMapping("/user")
public class UserController {
// 1. 基础映射:GET请求 /user/query → 匹配该方法
@RequestMapping(value = "/query", method = RequestMethod.GET)
public String queryUser(HttpServletRequest request, Model model) {
// 1. 获取请求参数(原生Servlet方式)
String userId = request.getParameter("id");
// 2. 封装数据到Model(传递到视图)
model.addAttribute("userId", userId);
model.addAttribute("username", "Java日记");
// 3. 返回视图名(视图解析器会解析为/WEB-INF/views/userInfo.jsp)
return "userInfo";
}
// 2. 简化参数绑定:直接将参数绑定到方法参数
@GetMapping("/detail") // @GetMapping = @RequestMapping(method=GET)
public String userDetail(@RequestParam("id") String userId,
@RequestParam(defaultValue = "18") Integer age,
Model model) {
model.addAttribute("msg", "用户ID:" + userId + ",年龄:" + age);
return "detail";
}
// 3. 路径变量:RESTful风格(URL中嵌入参数)
@GetMapping("/{id}/info")
public String restfulDemo(@PathVariable("id") String userId, Model model) {
model.addAttribute("restMsg", "RESTful风格:用户ID=" + userId);
return "restful";
}
// 4. POST请求映射(配合表单提交)
@PostMapping("/save") // @PostMapping = @RequestMapping(method=POST)
public String saveUser(@RequestParam("username") String username,
@RequestParam("password") String password,
Model model) {
model.addAttribute("saveMsg", "保存用户:" + username + " 成功");
// 重定向(不会经过视图解析器,需加redirect:前缀)
return "redirect:/user/success";
}
// 5. 跳转成功页
@GetMapping("/success")
public String success() {
return "success";
}
// 6. 响应JSON数据(@ResponseBody:返回值直接作为响应体,不解析为视图)
@GetMapping("/json")
@ResponseBody
public String getJson() {
return "{\"code\":200,\"msg\":\"success\"}";
}
}
3. 编写视图页面
(1)用户信息页:/WEB-INF/views/userInfo.jsp
jsp
java
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户信息</title>
</head>
<body>
<h1>用户信息页面</h1>
<p>用户ID:${userId}</p>
<p>用户名:${username}</p>
</body>
</html>
(2)表单页:/WEB-INF/views/form.jsp
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户表单</title>
</head>
<body>
<h1>用户注册</h1>
<form action="${pageContext.request.contextPath}/user/save" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
(3)成功页:/WEB-INF/views/success.jsp
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功页</title>
</head>
<body>
<h1>操作成功</h1>
<p>${saveMsg}</p>
</body>
</html>
4. 测试请求映射
(1)测试 GET 请求
访问:http://localhost:8080/user/query?id=1001效果:跳转到userInfo.jsp,展示用户 ID 和用户名。
(2)测试 RESTful 风格
访问:http://localhost:8080/user/1002/info效果:跳转到restful.jsp,展示 RESTful 风格的用户 ID。
(3)测试 POST 请求
- 访问:
http://localhost:8080/user/form(需新增 form 方法返回 form 视图); - 填写表单并提交,会跳转到
success.jsp,展示保存成功信息。
(4)测试 JSON 响应
访问:http://localhost:8080/user/json效果:直接返回 JSON 字符串,而非跳转页面。
5. 简化注解(Spring4.3+)
为了简化@RequestMapping的 method 属性,Spring 提供了专用注解:
| 简化注解 | 等价于 |
|---|---|
| @GetMapping | @RequestMapping(method=RequestMethod.GET) |
| @PostMapping | @RequestMapping(method=RequestMethod.POST) |
| @PutMapping | @RequestMapping(method=RequestMethod.PUT) |
| @DeleteMapping | @RequestMapping(method=RequestMethod.DELETE) |
五、核心避坑指南
1. 常见问题与解决方案
| 问题现象 | 根因 | 解决方案 |
|---|---|---|
| 404 错误(页面找不到) | 1. 视图解析器前缀 / 后缀配置错误;2. JSP 文件路径错误;3. 返回视图名错误 | 1. 核对 prefix/suffix;2. 检查 JSP 是否在 / WEB-INF/views/ 下;3. 核对返回的视图名 |
| 405 错误(请求方法不允许) | @RequestMapping 的 method 与实际请求方式不匹配 | 1. 检查 method 属性;2. 改用 @GetMapping/@PostMapping |
| 静态资源(CSS/JS)无法访问 | DispatcherServlet 拦截了静态资源 | 添加<mvc:default-servlet-handler/>放行静态资源 |
| POST 请求中文乱码 | 未配置字符编码过滤器 | 在 web.xml 中配置 CharacterEncodingFilter |
| @ResponseBody 返回中文乱码 | 响应编码未设置 | 在<mvc:annotation-driven/>中配置消息转换器,设置 UTF-8 |
2. 最佳实践
- URL 设计:遵循 RESTful 风格,用 URL 表示资源,用请求方式表示操作(GET 查询、POST 新增、PUT 修改、DELETE 删除);
- 参数绑定 :优先使用
@RequestParam/@PathVariable,而非原生 HttpServletRequest,代码更简洁; - 视图跳转 :
- 服务器内部跳转:返回视图名(如
return "userInfo"); - 重定向:加
redirect:前缀(如return "redirect:/user/success"); - 响应 JSON:加
@ResponseBody注解;
- 服务器内部跳转:返回视图名(如
- 包扫描 :
context:component-scan仅扫描 Controller 包(com.example.controller),避免扫描 Service/DAO,提升性能; - 静态资源 :将 CSS/JS/ 图片放在
webapp/static目录下,通过<mvc:default-servlet-handler/>放行。
六、今日实战小任务
- 基于实战案例,新增
@PutMapping("/{id}/update")和@DeleteMapping("/{id}/delete")方法,实现 RESTful 风格的修改和删除; - 新增一个登录页面(login.jsp),通过
@PostMapping("/login")处理登录请求,校验用户名密码(admin/123456),成功则重定向到首页,失败则返回登录页并提示错误信息; - 配置
@ResponseBody返回 JSON 时的中文乱码问题,通过<mvc:annotation-driven/>配置 StringHttpMessageConverter 的编码为 UTF-8。
总结
- SpringMVC 的核心是 DispatcherServlet 前端控制器,统一接收并分发请求,通过 HandlerMapping 匹配 Controller 方法,HandlerAdapter 调用方法,ViewResolver 解析视图;
@RequestMapping是请求映射的核心注解,支持类级和方法级配置,可指定 URL、请求方式、参数等,Spring4.3 + 提供@GetMapping/@PostMapping等简化注解;- 视图解析器通过前缀(prefix)和后缀(suffix)将视图名解析为具体的 JSP 路径,重定向需加
redirect:前缀,响应 JSON 需加@ResponseBody; - 核心避坑点:404/405 错误、静态资源访问、中文乱码,需重点关注配置文件和注解的正确性。
下一篇【Day43】预告:SpringMVC 参数绑定与返回值处理:从简单参数到复杂对象,关注专栏继续解锁 SpringMVC 核心功能~若本文对你有帮助,欢迎点赞 + 收藏 + 关注,你的支持是我更新的最大动力💖!