MVC快速入门
前言
什么是MVC
MVC英文是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计规范,本质上也是一种解耦。

- Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
- View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
- Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
什么是SpringMVC
而Spring Web MVC 则是一种基于Java 的实现了Web MVC 设计模式的请求驱动类型的轻量级Web 框架,即使用了MVC 架构模式的思想,将 web 层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是为了简化开 发,Spring Web MVC 也是要简化我们日常Web 开发的。
说白了,Spring MVC 就是 【接收请求】【响应数据】
Spring MVC 下一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。
常用组件:
前端控制器(DispatcherServlet):接收用户请求,给用户返回结果。
处理器映射器(HandlerMapping):根据请求的url路径,通过注解或者xml配置,寻找匹配的Handler。
处理器适配器(HandlerAdapter):Handler 的适配器,调用 handler 的方法处理请求。
处理器(Handler):执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装到ModelAndView对象中。
视图解析器(ViewResolver):将逻辑视图名解析成真正的视图View。
视图(View):接口类,实现类可支持不同的View类型(JSP、FreeMarker、Excel等)
MVC案例
基于webxml

maven引入
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.seven</groupId>
<artifactId>spring-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>07-spring-mvc-helloworld</artifactId>
<packaging>war</packaging>
<name>07-spring-mvc-helloworld Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>5.3.37</spring.version>
<servlet.version>4.0.1</servlet.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<build>
<finalName>07-spring-mvc-helloworld</finalName>
</build>
</project>业务代码编写
- entity的User类
@Data
@AllArgsConstructor
public class User {
private String name;
private int age;
}- dao层
@Repository
public class UserDaoImpl {
public List<User> findUserList() {
return Collections.singletonList(new User("seven", 18));
}
}- service层
@Service
public class UserServiceImpl {
@Autowired
private UserDaoImpl userDao;
public List<User> findUserList() {
return userDao.findUserList();
}
}- controller层
@Controller
public class UserController {
@Autowired
private UserServiceImpl userService;
@RequestMapping("/user")
public ModelAndView list(HttpServletRequest request, HttpServletResponse response) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("dateTime", new Date());
modelAndView.addObject("userList", userService.findUserList());
modelAndView.setViewName("userList"); // views目录下userList.jsp
return modelAndView;
}
}webapp下的web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<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_3_1.xsd"
version="3.1">
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>springmvc-demo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc-demo</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>springmvc.xml
web.xml中配置初始化参数contextConfigLocation,路径是classpath:springmvc.xml,因此文件直接创建在resources目录下
<?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">
<!-- 扫描注解 -->
<context:component-scan base-package="com.seven.springmvchelloworld"/>
<!-- 静态资源处理 -->
<mvc:default-servlet-handler/>
<!-- 开启SpringMVC注解 -->
<mvc:annotation-driven/>
<!-- 可以代替下边的配置,实际开发中一般使用上面的方式开启注解-->
<!--注解映射器 -->
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>-->
<!--注解适配器 -->
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>-->
<!-- 视图解析器 -->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>JSP视图
创建userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>User List</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<c:if test="${!empty userList}">
<table class="table table-bordered table-striped">
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<c:forEach items="${userList}" var="user">
<tr>
<td>${user.name}</td>
<td>${user.age}</td>
</tr>
</c:forEach>
</table>
</c:if>
</div>
</body>
</html>之后就是使用tomcat部署测试了,这块就不说了
纯注解版
无需配置xml文件,依靠注解和配置类完成配置,注意需要注意满足sevlet3.0规范
具体源码点击这里
这个不做过多讲解,真实项目的用得较少。因为若是老项目,就是基于webxml的,若是新项目,则直接上springboot了。
Spring MVC响应请求
直接返回ModelAndView对象
ModelAndView对象将数据模型和视图信息封装在一起。
@RequestMapping("/order")
public ModelAndView getOrderPage() {
// 1. 创建ModelAndView对象
ModelAndView mav = new ModelAndView();
// 2. 添加模型数据(相当于model.addAttribute)
Order order = orderService.getLatestOrder();
mav.addObject("currentOrder", order);
mav.addObject("pageTitle", "订单详情");
// 3. 设置视图名称
mav.setViewName("orderDetail"); // 视图解析器会处理为完整路径
return mav;
}返回视图名称(页面跳转)
该方法返回 "userDetail.jsp",并可以在页面上通过 ${user}获取数据
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/detail")
public String getUserDetail(Model model) {
// 模拟查询用户信息
User user = userService.findUserById(1);
// 将数据添加到Model中,会自动存入请求域
model.addAttribute("user", user);
// 返回逻辑视图名,视图解析器会将其拼接为 "/WEB-INF/views/userDetail.jsp"
return "userDetail.jsp";
}
}使用Map传递数据
该方法返回 "userDetail.jsp",并可以在页面上通过 ${user}获取数据
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/detail")
public String getUserDetail(Map<String, User> map) {
// 模拟查询用户信息
User user = userService.findUserById(1);
// 将数据添加到Model中,会自动存入请求域
map.addAttribute("user", user);
// 返回逻辑视图名,视图解析器会将其拼接为 "/WEB-INF/views/userDetail.jsp"
return "userDetail.jsp";
}
}返回void
这种方式绕过了SpringMVC的视图解析,提供了最大灵活性,但需要自行处理响应细节,与Servlet API耦合度高,一般不推荐作为主要方式
@RequestMapping("/raw")
public void handleRawResponse(HttpServletResponse response) throws IOException {
// 设置响应类型和编码
response.setContentType("text/plain; charset=UTF-8");
// 直接通过HttpServletResponse输出
PrintWriter out = response.getWriter();
out.write("这是一个直接输出的响应");
out.flush();
}重定向跳转
redirect:会让浏览器地址栏变为新的URL。注意,重定向是两次请求,原始请求域(request scope)中的数据会丢失。若要传递参数,可使用 RedirectAttributes
@PostMapping("/submit")
public String submitForm(LoginForm form) {
// ... 处理表单提交逻辑,如保存数据
boolean isSuccess = loginService.processLogin(form);
// 重定向到另一个地址,防止用户刷新浏览器导致表单重复提交
return "redirect:/login/success"; // 浏览器会向 "/login/success" 发起新请求
}
@GetMapping("/success")
public String successPage() {
return "success"; // 展示成功页面
}使用HttpServletResponse
此方式适用于文件下载、输出特定二进制内容等需要精细控制输出流的场景。它完全绕过了SpringMVC的视图解析机制
@RequestMapping("/download")
public void downloadFile(HttpServletResponse response) throws IOException {
// 设置响应头,告诉浏览器这是一个要下载的PDF文件
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=\"document.pdf\"");
// 获取文件流(此处为模拟)
byte[] fileContent = getFileContent();
// 通过ServletResponse的输出流直接写入数据
ServletOutputStream out = response.getOutputStream();
out.write(fileContent);
out.flush();
}直接返回数据(如JSON)
@ResponseBody注解是核心,它告诉Spring将方法返回值直接写入响应流。若项目中配置了消息转换器(如Jackson),可直接返回对象,Spring会自动将其转为JSON
@Controller
@RequestMapping("/api")
public class ApiController {
@RequestMapping(value = "/user", produces = "application/json;charset=UTF-8")
@ResponseBody // 关键注解:表明返回值直接作为HTTP响应体,不进行视图解析
public String getUserAsJson() {
User user = new User("张三", 25);
// 手动将对象转为JSON字符串(需Jackson等库)
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(user);
} catch (JsonProcessingException e) {
return "{\"error\": \"转换失败\"}";
}
// 更佳实践:直接返回对象,配置消息转换器自动转JSON(见后续说明)
}
}SringMVC接收数据
基本数据类型接收
@RestController
@RequestMapping("/api/user")
public class UserController {
/**
* 接收单个基本类型参数
* GET /api/user/detail?id=123
*/
@GetMapping("/detail")
public String getUserDetail(@RequestParam("id") Long userId) {
// @RequestParam将请求参数"id"映射到方法参数userId
return "用户ID: " + userId;
}
/**
* 参数可选,设置默认值
* GET /api/user/list 或 /api/user/list?page=2
*/
@GetMapping("/list")
public String getUserList(
@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
@RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
// required=false表示参数可选,defaultValue设置默认值
return String.format("第%d页,每页%d条", page, size);
}
/**
* 简化写法:参数名与方法参数名一致时可省略@RequestParam
* GET /api/user/simple?name=张三&age=25
*/
@GetMapping("/simple")
public String simpleParams(String name, Integer age) {
// 当请求参数名与方法参数名一致时,可以省略@RequestParam
return "姓名: " + name + ", 年龄: " + age;
}
}接收路径参数
@RestController
@RequestMapping("/api/product")
public class ProductController {
/**
* 接收路径参数
* GET /api/product/1001/category/2001
*/
@GetMapping("/{productId}/category/{categoryId}")
public String getProductInfo(
@PathVariable("productId") Long productId,
@PathVariable("categoryId") Long categoryId) {
// @PathVariable从URL路径中提取参数
return String.format("产品ID: %d, 分类ID: %d", productId, categoryId);
}
/**
* 正则表达式限制路径参数格式
* GET /api/product/2023-10-25
*/
@GetMapping("/{date:\\d{4}-\\d{2}-\\d{2}}")
public String getProductsByDate(@PathVariable String date) {
// 使用正则表达式限制日期格式
return "查询日期: " + date;
}
}对象接收(自动绑定)
接收简单对象参数
/**
* 用户查询参数对象
*/
public class UserQueryParams {
private String username;
private String email;
private Integer age;
private Date createTime;
// 必须提供getter和setter方法
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; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
@Override
public String toString() {
return String.format("UserQuery{username='%s', email='%s', age=%d}",
username, email, age);
}
}
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 对象接收 - GET请求
* GET /api/users/search?username=张三&email=zhang@example.com&age=25
*/
@GetMapping("/search")
public String searchUsers(UserQueryParams params) {
// Spring自动将请求参数绑定到对象属性
return "查询参数: " + params.toString();
}
/**
* 对象接收 - POST表单提交
* POST /api/users/create
* Content-Type: application/x-www-form-urlencoded
* Body: username=李四&email=li@example.com&age=30
*/
@PostMapping("/create")
public String createUser(UserQueryParams user) {
return "创建用户: " + user.toString();
}
}接收嵌套对象参数
/**
* 地址信息
*/
public class Address {
private String province;
private String city;
private String street;
// getter/setter省略...
}
/**
* 用户信息(包含嵌套对象)
*/
public class UserInfo {
private String name;
private Integer age;
private Address address; // 嵌套对象
// getter/setter...
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
}
@RestController
@RequestMapping("/api/profile")
public class ProfileController {
/**
* 接收嵌套对象参数
* GET /api/profile/update?name=王五&age=28&address.province=北京&address.city=北京市
*/
@PostMapping("/update")
public String updateProfile(UserInfo userInfo) {
// 使用点号语法接收嵌套对象属性
return String.format("用户: %s, 年龄: %d, 地址: %s-%s",
userInfo.getName(), userInfo.getAge(),
userInfo.getAddress().getProvince(),
userInfo.getAddress().getCity());
}
}数组接收
数组在表单中的应用
<!-- 前端HTML表单示例 -->
<form action="/api/products/batch-delete" method="post">
<!-- 多个同名的checkbox -->
<input type="checkbox" name="productIds" value="1001"> 产品A
<input type="checkbox" name="productIds" value="1002"> 产品B
<input type="checkbox" name="productIds" value="1003"> 产品C
<button type="submit">批量删除</button>
</form>@RestController
@RequestMapping("/api/products")
public class ProductController {
/**
* 批量删除产品
* POST /api/products/batch-delete
* Body: productIds=1001&productIds=1002&productIds=1003
*/
@PostMapping("/batch-delete")
public String batchDeleteProducts(@RequestParam Long[] productIds) {
return "删除的产品ID: " + Arrays.toString(productIds);
}
}集合接收(通过包装对象)
SpringMVC 不能直接在方法参数中接收集合,但可以通过对象包装的方式来接收。
/**
* 包装类,包含集合属性
*/
public class BatchOperation {
private List<Long> ids;
private List<String> names;
// getter/setter...
public List<Long> getIds() { return ids; }
public void setIds(List<Long> ids) { this.ids = ids; }
public List<String> getNames() { return names; }
public void setNames(List<String> names) { this.names = names; }
}
@RestController
@RequestMapping("/api/batch")
public class BatchController {
/**
* 接收集合参数 - 通过包装对象
* POST /api/batch/process
* 请求体格式1: ids=1&ids=2&ids=3
* 请求体格式2: names=Alice&names=Bob&names=Charlie
*/
@PostMapping("/process")
public String processBatch(BatchOperation operation) {
StringBuilder result = new StringBuilder();
if (operation.getIds() != null) {
result.append("ID列表: ").append(operation.getIds());
}
if (operation.getNames() != null) {
result.append("名称列表: ").append(operation.getNames());
}
return result.toString();
}
}自定义转换器
- 日期格式转换器
/**
* 自定义日期转换器
* 将字符串转换为Date对象
*/
@Component
public class StringToDateConverter implements Converter<String, Date> {
private static final String[] DATE_PATTERNS = {
"yyyy-MM-dd",
"yyyy/MM/dd",
"yyyy-MM-dd HH:mm:ss",
"yyyy/MM/dd HH:mm:ss"
};
@Override
public Date convert(String source) {
if (source == null || source.trim().isEmpty()) {
return null;
}
// 尝试多种日期格式
for (String pattern : DATE_PATTERNS) {
try {
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.setLenient(false); // 严格模式
return format.parse(source);
} catch (ParseException e) {
// 尝试下一种格式
continue;
}
}
throw new IllegalArgumentException("无效的日期格式: " + source +
",支持的格式: " + Arrays.toString(DATE_PATTERNS));
}
}- 枚举类型转换器
/**
* 用户状态枚举
*/
public enum UserStatus {
ACTIVE("活跃"),
INACTIVE("非活跃"),
DELETED("已删除");
private final String description;
UserStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 字符串到枚举转换器
*/
@Component
public class StringToUserStatusConverter implements Converter<String, UserStatus> {
@Override
public UserStatus convert(String source) {
if (source == null || source.trim().isEmpty()) {
return null;
}
// 不区分大小写匹配
for (UserStatus status : UserStatus.values()) {
if (status.name().equalsIgnoreCase(source)) {
return status;
}
}
// 也支持中文描述匹配
for (UserStatus status : UserStatus.values()) {
if (status.getDescription().equals(source)) {
return status;
}
}
throw new IllegalArgumentException("无效的用户状态: " + source);
}
}- 注册自定义转换器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private StringToDateConverter stringToDateConverter;
@Autowired
private StringToUserStatusConverter stringToUserStatusConverter;
/**
* 注册自定义转换器
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(stringToDateConverter);
registry.addConverter(stringToUserStatusConverter);
}
}- 在控制器中使用自定义转换
@RestController
@RequestMapping("/api/converter")
public class ConverterController {
/**
* 使用自定义日期转换器
* GET /api/converter/date?date=2023-10-25
*/
@GetMapping("/date")
public String handleDateParam(@RequestParam Date date) {
// Spring会自动使用我们注册的StringToDateConverter
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日");
return "转换后的日期: " + format.format(date);
}
/**
* 使用自定义枚举转换器
* GET /api/converter/status?status=ACTIVE
* GET /api/converter/status?status=活跃
*/
@GetMapping("/status")
public String handleStatusParam(@RequestParam UserStatus status) {
return "用户状态: " + status.getDescription();
}
/**
* 在对象中使用自定义转换
* GET /api/converter/user?name=张三&createTime=2023-10-25 14:30:00&status=INACTIVE
*/
@GetMapping("/user")
public String handleUserObject(UserQuery userQuery) {
// UserQuery对象中包含Date和UserStatus属性
return String.format("用户: %s, 创建时间: %s, 状态: %s",
userQuery.getName(),
userQuery.getCreateTime(),
userQuery.getStatus().getDescription());
}
}
/**
* 用户查询对象(包含需要自定义转换的属性)
*/
class UserQuery {
private String name;
private Date createTime; // 需要自定义转换
private UserStatus status; // 需要自定义转换
// getter/setter...
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
public UserStatus getStatus() { return status; }
public void setStatus(UserStatus status) { this.status = status; }
}SpringMVC其它使用
视图解析器添加前后缀
配置视图解析器可以免去重复书写视图文件路径的前后缀。
- xml 或者Java Config
<!-- 视图解析器 -->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/views/"/>
<property name="suffix" value=".jsp"/>
</bean>@Configuration
public class ViewConfig {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
// 设置所有视图文件所在的公共前缀
resolver.setPrefix("/views/");
// 设置视图文件的公共后缀
resolver.setSuffix(".jsp");
return resolver;
}
}- 控制器代码
此配置后,控制器返回的 "index"会被自动补全为 /views/index.jsp,极大简化了视图管理
@Controller
public class HomeController {
@RequestMapping("/home")
public String home() {
// 控制器中只需返回逻辑视图名 "index"
// 视图解析器会自动拼接为 "/WEB-INF/views/index.jsp"
return "index";
}
}中文乱码问题处理
<?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">
<!-- 1. 配置字符编码过滤器(必须放在第一个) -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<!-- 设置编码为UTF-8 -->
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<!-- 强制请求和响应都使用UTF-8编码 -->
<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>静态资源处理
默认情况下,DispatcherServlet 会拦截所有请求,包括静态资源请求,这会导致静态资源无法正常访问。因此,我们需要配置 Spring MVC 以允许容器直接提供静态资源。
有两种主要方式来处理静态资源:
- 使用
<mvc:resources />标签(XML 配置) - 使用
WebMvcConfigurer的addResourceHandlers方法(Java 配置)
另外,还可以使用 <mvc:default-servlet-handler />来允许容器默认的 Servlet 处理静态资源。
- XML 配置方式-使用默认Servlet处理(简单方式)
<?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: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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 启用默认Servlet处理静态资源 -->
<mvc:default-servlet-handler/>
<!-- 启用注解驱动 -->
<mvc:annotation-driven/>
<!-- 控制器扫描 -->
<context:component-scan base-package="com.example.controller"/>
</beans>- 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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 启用注解驱动 -->
<mvc:annotation-driven/>
<!-- 控制器扫描 -->
<context:component-scan base-package="com.example.controller"/>
<!-- 静态资源映射配置 -->
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/uploads/**" location="file:/var/uploads/"/>
<!-- 带版本控制的资源映射 -->
<mvc:resources mapping="/resources/**" location="/resources/" cache-period="3600"/>
<!-- WebJars支持 -->
<mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/>
</beans>- JavaConfig 配置方式
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 配置静态资源处理
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 1. 类路径下的静态资源
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCachePeriod(3600); // 缓存1小时
// 2. Web根目录下的资源
registry.addResourceHandler("/css/**")
.addResourceLocations("/css/");
registry.addResourceHandler("/js/**")
.addResourceLocations("/js/");
registry.addResourceHandler("/images/**")
.addResourceLocations("/images/");
// 3. 外部文件系统资源
registry.addResourceHandler("/uploads/**")
.addResourceLocations("file:/var/uploads/");
// 4. WebJars支持
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
