
Spring MVC 详解
Spring MVC 是 Spring Framework 的一个模块,专门用于开发 Web 应用程序。它实现了 MVC(Model-View-Controller)设计模式,使得开发 Web 应用变得简单高效。本文将详细介绍 Spring MVC 的核心概念、工作原理以及实际应用。
目录
[什么是 Spring MVC](#什么是-spring-mvc)
[Spring MVC 的核心组件](#spring-mvc-的核心组件)
[Spring MVC 的工作流程](#spring-mvc-的工作流程)
[Spring MVC 的配置方式](#spring-mvc-的配置方式)
[Spring MVC 的常用注解](#spring-mvc-的常用注解)
[Spring MVC 的数据绑定](#spring-mvc-的数据绑定)
[Spring MVC 的视图解析](#spring-mvc-的视图解析)
[Spring MVC 的异常处理](#spring-mvc-的异常处理)
[Spring MVC 的拦截器](#spring-mvc-的拦截器)
[Spring MVC 与 RESTful API](#spring-mvc-与-restful-api)
[Spring MVC 的文件上传](#spring-mvc-的文件上传)
[Spring MVC 的实际应用案例](#spring-mvc-的实际应用案例)
[总结与展望](#总结与展望)
什么是 Spring MVC
Spring MVC 是一个基于 Java 的实现了 MVC 设计模式的请求驱动类型的轻量级 Web 框架,通过把 Model、View、Controller 分离,将 Web 层进行职责解耦,把复杂的 Web 应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
MVC 设计模式
Model(模型):负责存储数据和处理用户请求的业务逻辑
View(视图):负责展示数据给用户
Controller(控制器):负责接收用户请求并调用模型和视图完成用户请求
Spring MVC 的核心组件
Spring MVC 框架由以下几个核心组件组成:
1. DispatcherServlet:前端控制器,是整个 Spring MVC 的核心。负责接收请求、分发请求、处理响应。
2. HandlerMapping:处理器映射器,根据请求的 URL 找到对应的处理器(Handler)。
3. HandlerAdapter:处理器适配器,按照特定的规则去执行处理器。
4. Handler:处理器,也称为 Controller,负责具体的业务处理逻辑。
5. ViewResolver:视图解析器,负责将处理结果生成 View 视图。
6. View:视图,负责将模型数据通过页面展示给用户。
Spring MVC 的工作流程
Spring MVC 的工作流程可以用以下步骤来描述:
1. 用户发送请求至前端控制器 DispatcherServlet
2. DispatcherServlet 收到请求后,调用 HandlerMapping 处理器映射器
3. 处理器映射器根据请求 URL 找到具体的处理器,生成处理器对象及处理器拦截器(如果有),一并返回给 DispatcherServlet
4. DispatcherServlet 通过 HandlerAdapter 处理器适配器调用处理器
5. 执行处理器(Controller,也叫后端控制器)
6. Controller 执行完成返回 ModelAndView
7. HandlerAdapter 将 Controller 执行结果 ModelAndView 返回给 DispatcherServlet
8. DispatcherServlet 将 ModelAndView 传给 ViewResolver 视图解析器
9. ViewResolver 解析后返回具体 View
10. DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
11. DispatcherServlet 响应用户

Spring MVC 的配置方式
Spring MVC 提供了两种配置方式:XML 配置和 Java 配置。
XML 配置方式
<!-- web.xml 配置 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>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>
<!-- spring-mvc.xml 配置视图解析器等 -->
<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.example.controller"/>
<!-- 开启 SpringMVC 注解驱动 -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
Java 配置方式
// WebConfig.java
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}
}
// WebAppInitializer.java
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Spring MVC 的常用注解
Spring MVC 提供了丰富的注解,使得开发更加简便:
1. @Controller:标识一个类为控制器
2. @RequestMapping:映射请求路径
3. @GetMapping:映射 GET 请求
4. @PostMapping:映射 POST 请求
5. @PutMapping:映射 PUT 请求
6. @DeleteMapping:映射 DELETE 请求
7. @PathVariable:获取 URL 中的动态参数
8. @RequestParam:获取请求参数
9. @RequestBody:获取请求体内容
10. @ResponseBody:将返回值直接写入响应体
11. @RestController:@Controller 和 @ResponseBody 的组合注解
12. @ModelAttribute:绑定请求参数到模型对象
示例代码
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public String getUserById(@PathVariable("id") Long id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "userDetail";
}
@PostMapping
public String createUser(@RequestParam String username, @RequestParam String password) {
userService.createUser(username, password);
return "redirect:/user/list";
}
@GetMapping("/json/{id}")
@ResponseBody
public User getUserJson(@PathVariable("id") Long id) {
return userService.findById(id);
}
}
Spring MVC 的数据绑定
Spring MVC 提供了强大的数据绑定功能,可以将请求参数绑定到 Java 对象上。
简单类型绑定
@GetMapping("/hello")
public String hello(@RequestParam String name) {
return "Hello, " + name;
}
对象绑定
@PostMapping("/register")
public String register(User user) {
userService.register(user);
return "redirect:/login";
}
集合类型绑定
@PostMapping("/batch")
public String batchUpdate(@RequestParam("ids") List<Long> ids) {
userService.batchDelete(ids);
return "redirect:/user/list";
}
日期类型绑定
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
@PostMapping("/event")
public String createEvent(@RequestParam("eventDate") Date eventDate, @RequestParam String name) {
eventService.createEvent(name, eventDate);
return "redirect:/event/list";
}
Spring MVC 的视图解析
Spring MVC 支持多种视图技术,包括 JSP、Thymeleaf、FreeMarker 等。
JSP 视图
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello, Spring MVC!");
return "hello"; // 返回视图名称,会被解析为 /WEB-INF/views/hello.jsp
}
Thymeleaf 视图
// 配置 Thymeleaf 视图解析器
@Bean
public ViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
return engine;
}
@Bean
public ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
Spring MVC 的异常处理
Spring MVC 提供了多种异常处理机制:
@ExceptionHandler 注解
@Controller
public class UserController {
@ExceptionHandler(UserNotFoundException.class)
public ModelAndView handleUserNotFoundException(UserNotFoundException ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("message", ex.getMessage());
return mav;
}
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
throw new UserNotFoundException("User not found with id: " + id);
}
return "userDetail";
}
}
全局异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("message", "An error occurred: " + ex.getMessage());
return mav;
}
@ExceptionHandler(UserNotFoundException.class)
public ModelAndView handleUserNotFoundException(UserNotFoundException ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("message", ex.getMessage());
return mav;
}
}
SimpleMappingExceptionResolver
@Bean
public SimpleMappingExceptionResolver exceptionResolver() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.put("java.lang.Exception", "error");
mappings.put("com.example.exception.UserNotFoundException", "user-not-found");
resolver.setExceptionMappings(mappings);
resolver.setDefaultErrorView("default-error");
return resolver;
}
Spring MVC 的拦截器
Spring MVC 的拦截器(Interceptor)类似于 Servlet 的过滤器(Filter),用于在请求处理的前后执行一些操作。
定义拦截器
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在请求处理之前执行,返回 true 表示继续处理,返回 false 表示中断处理
String token = request.getHeader("Authorization");
if (token == null || !tokenService.isValid(token)) {
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 {
// 在整个请求处理完毕后执行
}
}
注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/login");
}
}
Spring MVC 与 RESTful API
Spring MVC 对 RESTful API 的开发提供了良好的支持。
RESTful 控制器
@RestController
@RequestMapping("/api/users")
public class UserRestController {
@Autowired
private UserService userService;
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
if (!userService.exists(id)) {
return ResponseEntity.notFound().build();
}
user.setId(id);
User updatedUser = userService.update(user);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
if (!userService.exists(id)) {
return ResponseEntity.notFound().build();
}
userService.delete(id);
return ResponseEntity.noContent().build();
}
}
Spring MVC 的文件上传
Spring MVC 提供了简单的文件上传功能。
配置 MultipartResolver
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(5242880); // 5MB
return resolver;
}
处理文件上传
@Controller
public class FileUploadController {
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "请选择一个文件上传");
return "redirect:/uploadForm";
}
try {
byte[] bytes = file.getBytes();
Path path = Paths.get("uploads/" + file.getOriginalFilename());
Files.write(path, bytes);
redirectAttributes.addFlashAttribute("message", "文件上传成功: " + file.getOriginalFilename());
} catch (IOException e) {
e.printStackTrace();
redirectAttributes.addFlashAttribute("message", "文件上传失败: " + e.getMessage());
}
return "redirect:/uploadForm";
}
@GetMapping("/uploadForm")
public String showUploadForm() {
return "uploadForm";
}
}
Spring MVC 的实际应用案例
下面是一个简单的用户管理系统的示例:
实体类
public class User {
private Long id;
private String username;
private String email;
private String password;
// 构造函数、getter 和 setter 方法
}
服务层
@Service
public class UserService {
private Map<Long, User> users = new ConcurrentHashMap<>();
private AtomicLong idGenerator = new AtomicLong();
public List<User> findAll() {
return new ArrayList<>(users.values());
}
public User findById(Long id) {
return users.get(id);
}
public User save(User user) {
Long id = idGenerator.incrementAndGet();
user.setId(id);
users.put(id, user);
return user;
}
public User update(User user) {
users.put(user.getId(), user);
return user;
}
public void delete(Long id) {
users.remove(id);
}
public boolean exists(Long id) {
return users.containsKey(id);
}
}
控制器
@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public String listUsers(Model model) {
List<User> users = userService.findAll();
model.addAttribute("users", users);
return "userList";
}
@GetMapping("/{id}")
public String viewUser(@PathVariable Long id, Model model) {
User user = userService.findById(id);
if (user == null) {
throw new RuntimeException("User not found with id: " + id);
}
model.addAttribute("user", user);
return "userDetail";
}
@GetMapping("/new")
public String newUserForm(Model model) {
model.addAttribute("user", new User());
return "userForm";
}
@PostMapping
public String createUser(@ModelAttribute User user, BindingResult result) {
if (result.hasErrors()) {
return "userForm";
}
userService.save(user);
return "redirect:/users";
}
@GetMapping("/{id}/edit")
public String editUserForm(@PathVariable Long id, Model model) {
User user = userService.findById(id);
if (user == null) {
throw new RuntimeException("User not found with id: " + id);
}
model.addAttribute("user", user);
return "userForm";
}
@PostMapping("/{id}")
public String updateUser(@PathVariable Long id, @ModelAttribute User user, BindingResult result) {
if (result.hasErrors()) {
return "userForm";
}
user.setId(id);
userService.update(user);
return "redirect:/users";
}
@GetMapping("/{id}/delete")
public String deleteUser(@PathVariable Long id) {
userService.delete(id);
return "redirect:/users";
}
}
视图模板(使用 JSP)
<!-- userList.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户列表</title>
</head>
<body>
<h1>用户列表</h1>
<a href="<c:url value='/users/new'/>">添加用户</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="<c:url value='/users/${user.id}'/>">查看</a>
<a href="<c:url value='/users/${user.id}/edit'/>">编辑</a>
<a href="<c:url value='/users/${user.id}/delete'/>">删除</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
参考资料
1. Spring 官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
2. Spring MVC 教程:https://www.baeldung.com/spring-mvc-tutorial
3. Spring Boot 与 Spring MVC:https://spring.io/guides/gs/serving-web-content/
- 感谢你赐予我前进的力量