Spring MVC

核心执行流程
Spring MVC 处理请求时,主要组件及职责如下:
DispatcherServlet:前端控制器,统一接收请求并协调后续处理流程。HandlerMapping:根据请求地址查找对应的处理器方法。HandlerAdapter:以统一方式调用处理器,并完成参数解析等工作。Handler:实际处理请求的控制器方法。ModelAndView:同时封装模型数据和逻辑视图名。ViewResolver:把逻辑视图名解析为具体视图。View:负责渲染响应内容。
搭建传统 Spring MVC 项目
创建 Maven Web 工程
引入 Spring MVC 依赖。以下版本仅对应原课程示例环境:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
配置 DispatcherServlet
在 web.xml 中注册前端控制器:
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>/ 表示让 DispatcherServlet 处理除容器内部特殊请求之外的大部分请求,同时仍可由默认 Servlet 处理静态资源。通常不建议配置为 /*,因为它可能影响 JSP 转发等容器行为。
创建 Spring MVC 配置文件
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.hyxy.controller"/>
<mvc:annotation-driven/>
</beans>创建控制器
@Controller
public class TestController {
@RequestMapping("/test")
@ResponseBody
public String test() {
return "test";
}
}
启动服务器后访问 /test,响应体中会返回 test。
请求映射
@RequestMapping
@RequestMapping 可以修饰类和方法。修饰类时通常表示统一的地址前缀;修饰方法时表示具体请求映射。
常用属性包括:
value或path:请求地址。method:允许的请求方法。params:要求请求中包含或不包含指定参数。headers:根据请求头进一步限制映射。consumes:限制请求体的媒体类型。produces:声明响应媒体类型。
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping(value = "/add", method = RequestMethod.POST)
@ResponseBody
public String add() {
return "add";
}
}在实际项目中,也可使用语义更明确的组合注解,例如 @GetMapping、@PostMapping、@PutMapping 和 @DeleteMapping。
请求参数绑定
绑定简单参数
请求参数名与方法参数名一致时,Spring MVC 可以自动绑定。若编译时未保留参数名,或希望显式指定参数名称,应使用 @RequestParam。
@GetMapping("/user")
@ResponseBody
public String getUser(
@RequestParam(name = "name") String username,
@RequestParam(name = "age", required = false, defaultValue = "0") Integer age) {
return username + ":" + age;
}
使用基本类型接收可选参数时,参数缺失会导致类型转换失败,因此可选数字参数一般使用包装类型。
绑定路径变量
@GetMapping("/add/{name}/{pwd}")
@ResponseBody
public String add(
@PathVariable("name") String username,
@PathVariable("pwd") String password) {
return username + ":" + password;
}
例如访问 http://localhost:8088/mvc/test/add/tom/123,username 的值为 tom,password 的值为 123。
读取请求头和 Cookie
@GetMapping("/header")
@ResponseBody
public String header(
@RequestHeader("Accept") String accept,
@CookieValue(value = "username", defaultValue = "rose") String username) {
return accept + ":" + username;
}
绑定 Java 对象
请求参数名与对象属性名一致时,可以把参数绑定到对象中。
@PostMapping("/emp")
@ResponseBody
public Emp add(Emp emp) {
return emp;
}
对于关联对象,可使用属性路径传参。例如 Emp 中包含 Dept dept 时,请求参数可写成 dept.deptName=开发部。
public class Emp {
private String empNo;
private String empName;
private Dept dept;
// getter 和 setter
}
public class Dept {
private String deptName;
// getter 和 setter
}获取 Servlet API 对象
需要访问原始请求或会话时,可直接在控制器方法参数中声明。
@GetMapping("/request-info")
@ResponseBody
public String requestInfo(HttpServletRequest request, HttpSession session) {
session.setAttribute("method", request.getMethod());
return request.getMethod();
}
业务代码不应过度依赖 Servlet API。能通过注解和参数绑定完成的场景,应优先使用 Spring MVC 的参数解析机制。
视图跳转与模型数据
返回逻辑视图名
@GetMapping("/books")
public String books(Model model) {
model.addAttribute("bookList", List.of("Java", "Spring"));
return "book/list";
}
配合视图解析器时,book/list 会被解析为具体页面路径。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
返回 ModelAndView
@GetMapping("/detail")
public ModelAndView detail() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", "Spring MVC");
modelAndView.setViewName("book/detail");
return modelAndView;
}
静态资源处理
可以显式配置静态资源映射:
<mvc:annotation-driven/>
<mvc:resources mapping="/html/**" location="/html/"/>
也可使用默认 Servlet 处理未匹配的静态资源:
<mvc:default-servlet-handler/>
前后端分离开发
@RequestBody
@RequestBody 会读取请求体,并通过消息转换器把 JSON 转换为 Java 对象。请求头通常应包含 Content-Type: application/json。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/emp")
public Emp save(@RequestBody Emp emp) {
return emp;
}
}

JSON 字段名应与 Java 属性名匹配。字段命名不一致时,可以使用 Jackson 注解进行映射。
@ResponseBody
@ResponseBody 表示把方法返回值写入响应体。返回对象时,Spring MVC 会通过消息转换器将其序列化为 JSON;返回字符串时,通常直接写入文本内容。
@GetMapping("/get")
@ResponseBody
public User get() {
User user = new User();
user.setUsername("jack");
user.setPassword("123456");
return user;
}
@RestController
@RestController 相当于类级别的 @Controller 与 @ResponseBody 组合,适合以 JSON 为主要响应内容的控制器。
统一响应对象
public class Result<T> {
private boolean success;
private T data;
private String message;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.success = true;
result.data = data;
return result;
}
public static <T> Result<T> failure(String message) {
Result<T> result = new Result<>();
result.success = false;
result.message = message;
return result;
}
// getter 和 setter
}跨域请求
浏览器的同源策略会限制脚本访问不同源的资源。源由协议、主机和端口共同确定,任意一项不同都属于跨源请求。
使用 @CrossOrigin
@RestController
@RequestMapping("/book")
@CrossOrigin(origins = "http://localhost:8081")
public class BookController {
}
全局配置 CORS
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8081")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}开启凭据时,不能把允许来源简单配置为通配符。生产环境应只开放实际需要的来源、方法和请求头。
前端代理与 Nginx 反向代理
开发环境可以通过前端开发服务器代理接口请求;生产环境通常使用 Nginx 将前端静态资源和后端接口统一到同一站点下。


反向代理只改变请求转发方式,不会替代后端自身的鉴权、权限校验和输入校验。
文件上传
配置文件解析器
在传统 Spring MVC 项目中,可配置 CommonsMultipartResolver:
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5242880"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
表单应使用 POST 请求,并设置 enctype="multipart/form-data"。
编写上传接口
@RestController
public class FileUploadController {
private static final Path UPLOAD_DIR = Paths.get("D:/Lesson/uploadFile");
@PostMapping("/upload")
public Result<String> upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.failure("上传文件不能为空");
}
String originalName = file.getOriginalFilename();
String safeName = originalName == null
? UUID.randomUUID().toString()
: Paths.get(originalName).getFileName().toString();
try {
Files.createDirectories(UPLOAD_DIR);
Path target = UPLOAD_DIR.resolve(safeName).normalize();
if (!target.startsWith(UPLOAD_DIR)) {
return Result.failure("文件名不合法");
}
file.transferTo(target);
return Result.success(safeName);
} catch (IOException e) {
return Result.failure(e.getMessage());
}
}
}不要直接信任客户端提交的原始文件名。实际项目还应检查文件大小、扩展名、MIME 类型和存储权限,并避免把上传目录暴露为可执行目录。

喜欢的话,留下你的评论吧~