Spring MVC 概述

什么是 Spring MVC

Spring MVC 是 Spring 框架的一部分用于构建 Web 应用程序它遵循 Model-View-ControllerMVC设计模式将业务逻辑数据和展示分离

  • 核心功能
    • 请求映射将 URL 映射到控制器方法
    • 数据绑定自动将请求参数绑定到 Java 对象
    • 视图解析支持多种视图技术JSPThymeleafFreeMarker
    • 拦截器提供请求预处理和后处理
  • 主要优势
    • 清晰的角色划分控制器验证器命令对象等
    • 强大的配置支持 XML 和注解配置
    • 可适配的处理器映射和视图解析
    • 灵活的模型传递

Spring MVC 的核心概念

MVC 架构

组件 职责 示例
Model模型 封装业务数据和逻辑 EntityServiceDAO
View视图 展示数据给用户 JSPThymeleafJSON
Controller控制器 处理用户请求协调 Model 和 View @Controller 类

核心组件

  • DispatcherServlet前端控制器所有请求的入口
  • HandlerMapping处理器映射找到对应的 Controller
  • Controller处理请求的控制器
  • ModelAndView包含模型数据和视图信息
  • ViewResolver视图解析器解析视图名称
  • View具体的视图实现

Spring MVC 的工作原理

请求处理流程

1
2
3
4
5
6
7
1. 用户发送请求到 DispatcherServlet
2. DispatcherServlet 查询 HandlerMapping 找到对应的 Controller
3. DispatcherServlet 调用 Controller 处理请求
4. Controller 执行业务逻辑返回 ModelAndView
5. DispatcherServlet 通过 ViewResolver 解析视图
6. View 渲染模型数据
7. DispatcherServlet 将响应返回给用户

组件交互

1
2
3
4
5
6
7
客户端 → DispatcherServlet → HandlerMapping → Controller

ModelAndView

ViewResolver → View

响应客户端

环境搭建

添加依赖

Maven 依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>

<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>

<!-- JSP 支持可选 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

配置 Spring MVC

web.xml 配置传统方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?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">

<!-- 配置 DispatcherServlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/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>

<!-- 字符编码过滤器 -->
<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>
</web-app>

spring-mvc.xml 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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">

<!-- 启用注解驱动 -->
<mvc:annotation-driven/>

<!-- 扫描 Controller -->
<context:component-scan base-package="com.example.controller"/>

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>

<!-- 静态资源处理 -->
<mvc:resources mapping="/static/**" location="/static/"/>
</beans>

Java Config 配置推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {

// 视图解析器
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}

// 静态资源处理
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("/static/");
}
}

Servlet 初始化器替代 web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{AppConfig.class};
}

@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}

@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}

创建示例项目

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.example.entity;

public class User {
private Integer id;
private String username;
private String email;

// 构造方法
public User() {}

public User(Integer id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}

// Getter 和 Setter
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }

public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }

public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.example.controller;

import com.example.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("/users")
public class UserController {

private List<User> users = new ArrayList<>();

public UserController() {
users.add(new User(1, "张三", "zhangsan@example.com"));
users.add(new User(2, "李四", "lisi@example.com"));
}

// 查询列表
@GetMapping
public String list(Model model) {
model.addAttribute("users", users);
return "user/list";
}

// 查询详情
@GetMapping("/{id}")
public String detail(@PathVariable Integer id, Model model) {
User user = users.stream()
.filter(u -> u.getId().equals(id))
.findFirst()
.orElse(null);
model.addAttribute("user", user);
return "user/detail";
}

// 显示表单
@GetMapping("/add")
public String showAddForm(Model model) {
model.addAttribute("user", new User());
return "user/form";
}

// 新增
@PostMapping
public String add(User user) {
user.setId(users.size() + 1);
users.add(user);
return "redirect:/users";
}

// 删除
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
users.removeIf(u -> u.getId().equals(id));
return "redirect:/users";
}
}

JSP 视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- WEB-INF/views/user/list.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>用户列表</title>
</head>
<body>
<h1>用户列表</h1>
<a href="${pageContext.request.contextPath}/users/add">新增用户</a>
<table border="1">
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>操作</th>
</tr>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.email}</td>
<td>
<a href="${pageContext.request.contextPath}/users/${user.id}">详情</a>
<a href="${pageContext.request.contextPath}/users/delete/${user.id}">删除</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>

Controller

基本用法

@Controller 和 @RestController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// @Controller返回视图名称
@Controller
public class PageController {

@GetMapping("/home")
public String home() {
return "home"; // 返回视图名称
}
}

// @RestController返回 JSON 数据相当于 @Controller + @ResponseBody
@RestController
@RequestMapping("/api/users")
public class UserApiController {

@GetMapping
public List<User> list() {
return userService.findAll(); // 直接返回 JSON
}
}

请求映射

@RequestMapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Controller
@RequestMapping("/users")
public class UserController {

// GET /users
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list() {
return "user/list";
}

// POST /users
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String add(User user) {
userService.save(user);
return "redirect:/users/list";
}
}

简化注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@GetMapping      // @RequestMapping(method = RequestMethod.GET)
@PostMapping // @RequestMapping(method = RequestMethod.POST)
@PutMapping // @RequestMapping(method = RequestMethod.PUT)
@DeleteMapping // @RequestMapping(method = RequestMethod.DELETE)
@PatchMapping // @RequestMapping(method = RequestMethod.PATCH)

@GetMapping("/list")
public String list() {
return "user/list";
}

@PostMapping("/add")
public String add(User user) {
userService.save(user);
return "redirect:/users/list";
}

参数绑定

路径变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@GetMapping("/users/{id}")
public String detail(@PathVariable Integer id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "user/detail";
}

// 多个路径变量
@GetMapping("/users/{userId}/orders/{orderId}")
public String orderDetail(
@PathVariable Integer userId,
@PathVariable Integer orderId
) {
// ...
}

请求参数

1
2
3
4
5
6
7
8
9
10
11
12
@GetMapping("/search")
public String search(
@RequestParam String keyword,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String sort,
Model model
) {
List<User> users = userService.search(keyword, page, size, sort);
model.addAttribute("users", users);
return "user/list";
}

请求体

1
2
3
4
5
@PostMapping("/add")
public String add(@RequestBody User user) {
userService.save(user);
return "redirect:/users";
}

表单对象

1
2
3
4
5
6
7
8
@PostMapping("/register")
public String register(User user, BindingResult result) {
if (result.hasErrors()) {
return "register";
}
userService.save(user);
return "redirect:/login";
}

其他参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 请求头
@GetMapping("/info")
public String info(@RequestHeader("User-Agent") String userAgent) {
// ...
}

// Cookie
@GetMapping("/cookie")
public String cookie(@CookieValue("sessionId") String sessionId) {
// ...
}

// HttpServletRequest
@GetMapping("/request")
public String request(HttpServletRequest request) {
String ip = request.getRemoteAddr();
// ...
}

// HttpSession
@GetMapping("/session")
public String session(HttpSession session) {
session.setAttribute("user", user);
// ...
}

返回值

视图名称

1
2
3
4
@GetMapping("/home")
public String home() {
return "home"; // 返回视图名称
}

ModelAndView

1
2
3
4
5
6
7
@GetMapping("/users")
public ModelAndView list() {
ModelAndView mav = new ModelAndView();
mav.setViewName("user/list");
mav.addObject("users", userService.findAll());
return mav;
}

JSON 数据

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
public class UserApiController {

@GetMapping("/users")
public List<User> list() {
return userService.findAll();
}

@GetMapping("/users/{id}")
public User detail(@PathVariable Integer id) {
return userService.findById(id);
}
}

重定向

1
2
3
4
5
@PostMapping("/add")
public String add(User user) {
userService.save(user);
return "redirect:/users/list"; // 重定向
}

转发

1
2
3
4
@GetMapping("/forward")
public String forward() {
return "forward:/users/list"; // 转发
}

数据验证

Hibernate Validator

添加依赖

1
2
3
4
5
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>

实体类验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class User {

@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度必须在2-20之间")
private String username;

@Email(message = "邮箱格式不正确")
private String email;

@Min(value = 18, message = "年龄必须大于等于18")
@Max(value = 100, message = "年龄必须小于等于100")
private Integer age;

@NotNull(message = "性别不能为空")
private String gender;

// Getter 和 Setter
}

Controller 验证

1
2
3
4
5
6
7
8
9
10
@PostMapping("/add")
public String add(@Valid User user, BindingResult result, Model model) {
if (result.hasErrors()) {
// 验证失败返回表单页面
model.addAttribute("errors", result.getAllErrors());
return "user/form";
}
userService.save(user);
return "redirect:/users";
}

常用验证注解

注解 说明
@NotNull 不能为 null
@NotBlank 不能为 null 或空字符串
@NotEmpty 不能为 null 或空集合
@Size 字符串长度或集合大小
@Min / @Max 最小值 / 最大值
@Email 邮箱格式
@Pattern 正则表达式匹配
@Past / @Future 过去时间 / 未来时间

拦截器

自定义拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.example.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class AuthInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 请求处理前执行
HttpSession session = request.getSession();
Object user = session.getAttribute("user");

if (user == null) {
// 未登录跳转到登录页
response.sendRedirect("/login");
return false;
}

return true; // 放行
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
// 请求处理后执行
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 视图渲染后执行
}
}

配置拦截器

XML 配置

1
2
3
4
5
6
7
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/admin/**"/>
<mvc:exclude-mapping path="/admin/login"/>
<bean class="com.example.interceptor.AuthInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

Java Config 配置

1
2
3
4
5
6
7
8
9
10
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/login");
}
}

文件上传

配置文件上传解析器

XML 配置

1
2
3
4
5
<bean id="multipartResolver" 
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/> <!-- 10MB -->
<property name="defaultEncoding" value="UTF-8"/>
</bean>

添加依赖

1
2
3
4
5
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>

处理文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@PostMapping("/upload")
public String upload(
@RequestParam("file") MultipartFile file,
@RequestParam String description,
Model model
) {
if (!file.isEmpty()) {
try {
// 获取文件名
String fileName = file.getOriginalFilename();

// 保存文件
String uploadDir = "/uploads/";
File dest = new File(uploadDir + fileName);
file.transferTo(dest);

model.addAttribute("message", "上传成功");
} catch (IOException e) {
model.addAttribute("message", "上传失败: " + e.getMessage());
}
}

return "upload/result";
}

多文件上传

1
2
3
4
5
6
7
8
9
@PostMapping("/uploadMultiple")
public String uploadMultiple(@RequestParam("files") MultipartFile[] files) {
for (MultipartFile file : files) {
if (!file.isEmpty()) {
// 处理每个文件
}
}
return "redirect:/upload";
}

异常处理

局部异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller
public class UserController {

@ExceptionHandler(ArithmeticException.class)
public String handleArithmeticException(Exception e, Model model) {
model.addAttribute("error", e.getMessage());
return "error/arithmetic";
}

@ExceptionHandler(Exception.class)
public String handleException(Exception e, Model model) {
model.addAttribute("error", "系统异常");
return "error/general";
}
}

全局异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(BusinessException.class)
public ResponseEntity<Map<String, Object>> handleBusinessException(BusinessException e) {
Map<String, Object> result = new HashMap<>();
result.put("code", e.getCode());
result.put("message", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleException(Exception e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 500);
result.put("message", "系统异常");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}

RESTful API

RESTful 设计

HTTP 方法 URL 说明
GET /api/users 获取用户列表
GET /api/users/1 获取 ID 为 1 的用户
POST /api/users 创建新用户
PUT /api/users/1 更新 ID 为 1 的用户
DELETE /api/users/1 删除 ID 为 1 的用户

RESTful Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@RestController
@RequestMapping("/api/users")
public class UserApiController {

@Autowired
private UserService userService;

// 查询列表
@GetMapping
public ResponseEntity<List<User>> list() {
List<User> users = userService.findAll();
return ResponseEntity.ok(users);
}

// 查询详情
@GetMapping("/{id}")
public ResponseEntity<User> detail(@PathVariable Integer id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}

// 新增
@PostMapping
public ResponseEntity<User> create(@RequestBody User user) {
userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}

// 更新
@PutMapping("/{id}")
public ResponseEntity<Void> update(@PathVariable Integer id, @RequestBody User user) {
user.setId(id);
userService.update(user);
return ResponseEntity.noContent().build();
}

// 删除
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Integer id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}

最佳实践

项目结构规范

1
2
3
4
5
6
7
8
9
10
11
src/main/java/com/example/
├── controller/ # 控制器
├── service/ # 业务层
│ └── impl/ # 业务实现
├── dao/ # 数据访问层
├── entity/ # 实体类
├── dto/ # 数据传输对象
├── vo/ # 视图对象
├── interceptor/ # 拦截器
├── exception/ # 异常类
└── config/ # 配置类

统一返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;

public static <T> Result<T> success(T data) {
return new Result<>(200, "success", data);
}

public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
}

跨域配置

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class CorsConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.maxAge(3600);
}
}

常见问题

中文乱码

1
2
3
4
5
6
7
8
9
10
11
12
13
问题请求参数或响应内容中文乱码

解决方案
1. 配置 CharacterEncodingFilter
2. 在 web.xml 中添加
<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>
</filter>

404 Not Found

1
2
3
4
5
6
7
8
9
10
11
问题访问 URL 返回 404

错误原因
1. 请求路径错误
2. Controller 未正确配置
3. 包扫描范围不对

解决方案
1. 检查 @RequestMapping 路径
2. 确认 Controller 类上有 @Controller 注解
3. 确保 Controller 在扫描范围内

报错处理

💗💗 Spring MVC 报错No mapping found

1
2
3
4
5
6
7
8
9
10
11
12
错误信息
No mapping found for HTTP request with URI [/xxx] in DispatcherServlet

错误原因
1. URL 路径不匹配
2. Controller 未被扫描到
3. 缺少 @RequestMapping 注解

解决方案
1. 检查 URL 路径是否正确
2. 确认 @ComponentScan 包含 Controller 所在包
3. 添加正确的请求映射注解

💗💗 Spring MVC 报错415 Unsupported Media Type

1
2
3
4
5
6
7
8
9
10
错误信息
HTTP Status 415 – Unsupported Media Type

错误原因
1. Content-Type 不匹配
2. 缺少 Jackson 依赖

解决方案
1. 设置正确的 Content-Typeapplication/json
2. 添加 jackson-databind 依赖

学习资源

  • 视频
    • 尚硅谷 SpringMVC 教程https://www.bilibili.com/video/BV1Ry4y1574R
  • 官方文档
    • Spring MVC 官方文档https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
  • 书籍
    • Spring MVC 学习指南Paul Deck 著
    • 精通 Spring MVC 4Geoffroy Warin 著
  • 教程
    • Spring MVC 入门教程https://www.runoob.com/w3cnote/spring-mvc-tutorial.html
    • Baeldung Spring MVC 教程https://www.baeldung.com/category/spring-mvc/
  • 社区
    • Stack Overflow Spring MVC 标签https://stackoverflow.com/questions/tagged/spring-mvc