Spring MVC入门教程 - 从零开始学习Spring MVC框架
Spring MVC入门教程 - 从零开始学习Spring MVC框架
目录
- Spring MVC简介
- 环境搭建
- 第一个Spring MVC程序
- Spring MVC核心概念
- 控制器开发
- 请求映射
- 参数绑定
- 视图和视图解析器
- 数据绑定和验证
- 文件上传和下载
- 拦截器
- 异常处理
- RESTful API开发
- Spring MVC与Spring Boot集成
- 最佳实践
- 总结与进阶
1. Spring MVC简介
1.1 什么是Spring MVC
Spring MVC是Spring框架提供的用于构建Web应用程序的模块,它基于经典的MVC(Model-View-Controller)设计模式,提供了灵活、强大的Web开发能力。
MVC模式:
- Model(模型):数据和业务逻辑
- View(视图):用户界面展示
- Controller(控制器):处理用户请求,协调Model和View
1.2 Spring MVC的特点
- ✅ 松耦合:各个组件之间松耦合,易于测试和维护
- ✅ 灵活配置:支持XML配置和注解配置
- ✅ 强大的功能:请求映射、参数绑定、数据验证、文件上传等
- ✅ 与Spring集成:无缝集成Spring IoC和AOP
- ✅ RESTful支持:原生支持RESTful API开发
- ✅ 国际化支持:内置国际化功能
- ✅ 主题支持:支持主题切换
1.3 Spring MVC工作流程
1. 用户发送请求 → DispatcherServlet(前端控制器)
2. DispatcherServlet → HandlerMapping(处理器映射器)
3. HandlerMapping → Handler(处理器/Controller)
4. Handler → ModelAndView(模型和视图)
5. DispatcherServlet → ViewResolver(视图解析器)
6. ViewResolver → View(视图)
7. View → 响应返回给用户1.4 Spring MVC核心组件
- DispatcherServlet:前端控制器,统一处理请求
- HandlerMapping:处理器映射器,映射请求到处理器
- HandlerAdapter:处理器适配器,执行处理器
- Controller:处理器,处理业务逻辑
- ViewResolver:视图解析器,解析视图名称
- View:视图,渲染响应
2. 环境搭建
2.1 前置要求
- JDK 8或更高版本
- Maven 3.6+ 或 Gradle
- IDE(IntelliJ IDEA、Eclipse等)
- Tomcat服务器(或使用Spring Boot内嵌Tomcat)
2.2 创建Maven项目
使用IDE创建
- 打开IntelliJ IDEA
- File → New → Project
- 选择Maven,点击Next
- 填写GroupId和ArtifactId
- 点击Finish
使用命令行创建
mvn archetype:generate -DgroupId=com.example -DartifactId=springmvc-demo -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false2.3 添加依赖
在pom.xml中添加Spring MVC相关依赖:
<?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>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.3.23</spring.version>
<servlet.version>4.0.1</servlet.version>
<jsp.version>2.3.3</jsp.version>
<jstl.version>1.2</jstl.version>
</properties>
<dependencies>
<!-- Spring MVC核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<!-- JSP API -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<!-- JSON支持 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.5</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
<build>
<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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
</plugin>
</plugins>
</build>
</project>2.4 项目目录结构
springmvc-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── controller/
│ │ │ │ └── HelloController.java
│ │ │ ├── service/
│ │ │ │ └── UserService.java
│ │ │ ├── model/
│ │ │ │ └── User.java
│ │ │ └── config/
│ │ │ └── WebConfig.java
│ │ ├── resources/
│ │ │ └── springmvc-servlet.xml
│ │ └── webapp/
│ │ ├── WEB-INF/
│ │ │ ├── web.xml
│ │ │ └── views/
│ │ │ └── hello.jsp
│ │ └── index.jsp
└── pom.xml3. 第一个Spring MVC程序
3.1 配置web.xml
创建src/main/webapp/WEB-INF/web.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">
<display-name>Spring MVC Demo</display-name>
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 映射所有请求到DispatcherServlet -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器 -->
<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>3.2 配置Spring MVC
创建src/main/webapp/WEB-INF/springmvc-servlet.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">
<!-- 扫描Controller -->
<context:component-scan base-package="com.example.controller"/>
<!-- 启用注解驱动 -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<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>3.3 创建Controller
创建src/main/java/com/example/controller/HelloController.java:
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.ui.Model;
@Controller
public class HelloController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(Model model) {
model.addAttribute("message", "Hello, Spring MVC!");
return "hello";
}
}3.4 创建视图
创建src/main/webapp/WEB-INF/views/hello.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello Spring MVC</title>
</head>
<body>
<h1>${message}</h1>
</body>
</html>3.5 部署和运行
使用Maven打包
mvn clean package部署到Tomcat
- 将生成的war文件复制到Tomcat的webapps目录
- 启动Tomcat
访问应用
http://localhost:8080/springmvc-demo/hello
4. Spring MVC核心概念
4.1 DispatcherServlet
DispatcherServlet是Spring MVC的前端控制器,所有请求都经过它处理。
工作流程:
- 接收HTTP请求
- 查找HandlerMapping,确定处理请求的Controller
- 调用HandlerAdapter执行Controller
- 处理返回的ModelAndView
- 通过ViewResolver解析视图
- 渲染视图并返回响应
4.2 HandlerMapping
HandlerMapping负责将请求映射到对应的处理器。
类型:
- BeanNameUrlHandlerMapping:根据Bean名称映射
- SimpleUrlHandlerMapping:URL模式映射
- RequestMappingHandlerMapping:注解方式映射(最常用)
4.3 HandlerAdapter
HandlerAdapter负责执行处理器。
类型:
- SimpleControllerHandlerAdapter:执行Controller接口实现
- HttpRequestHandlerAdapter:执行HttpRequestHandler
- RequestMappingHandlerAdapter:执行@Controller注解的处理器
4.4 ViewResolver
ViewResolver负责将逻辑视图名解析为实际视图。
常用实现:
- InternalResourceViewResolver:JSP视图
- FreeMarkerViewResolver:FreeMarker模板
- ThymeleafViewResolver:Thymeleaf模板
- ContentNegotiatingViewResolver:内容协商视图解析器
4.5 Model和ModelAndView
Model:用于向视图传递数据
@Controller
public class UserController {
@RequestMapping("/user")
public String getUser(Model model) {
model.addAttribute("name", "Alice");
model.addAttribute("age", 25);
return "user";
}
}ModelAndView:同时包含模型数据和视图信息
@RequestMapping("/user")
public ModelAndView getUser() {
ModelAndView mav = new ModelAndView("user");
mav.addObject("name", "Alice");
mav.addObject("age", 25);
return mav;
}5. 控制器开发
5.1 @Controller注解
@Controller注解标识一个类为Spring MVC控制器。
@Controller
public class UserController {
// 控制器方法
}5.2 @RequestMapping注解
@RequestMapping用于映射请求到处理方法。
基本用法
@Controller
public class UserController {
// 映射GET请求到/user
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getUser() {
return "user";
}
// 映射POST请求到/user
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String createUser() {
return "success";
}
// 映射多个URL
@RequestMapping(value = {"/user", "/users"}, method = RequestMethod.GET)
public String listUsers() {
return "users";
}
}简化注解
@Controller
public class UserController {
// @GetMapping等价于@RequestMapping(method = RequestMethod.GET)
@GetMapping("/user")
public String getUser() {
return "user";
}
// @PostMapping等价于@RequestMapping(method = RequestMethod.POST)
@PostMapping("/user")
public String createUser() {
return "success";
}
// @PutMapping
@PutMapping("/user/{id}")
public String updateUser(@PathVariable Long id) {
return "success";
}
// @DeleteMapping
@DeleteMapping("/user/{id}")
public String deleteUser(@PathVariable Long id) {
return "success";
}
}类级别和方法级别
@Controller
@RequestMapping("/user") // 类级别,所有方法都以此开头
public class UserController {
@GetMapping("/list") // 实际路径:/user/list
public String list() {
return "user/list";
}
@GetMapping("/detail") // 实际路径:/user/detail
public String detail() {
return "user/detail";
}
}5.3 返回值类型
返回String(视图名)
@GetMapping("/user")
public String getUser() {
return "user"; // 返回视图名
}返回ModelAndView
@GetMapping("/user")
public ModelAndView getUser() {
ModelAndView mav = new ModelAndView("user");
mav.addObject("name", "Alice");
return mav;
}返回void(使用HttpServletResponse)
@GetMapping("/user")
public void getUser(HttpServletResponse response) throws IOException {
response.getWriter().write("User Info");
}返回ResponseEntity(RESTful)
@GetMapping("/user")
public ResponseEntity<User> getUser() {
User user = new User("Alice", 25);
return ResponseEntity.ok(user);
}返回@ResponseBody
@GetMapping("/user")
@ResponseBody
public User getUser() {
return new User("Alice", 25);
}6. 请求映射
6.1 URL路径映射
精确匹配
@GetMapping("/user/detail")
public String detail() {
return "user/detail";
}路径变量
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
// 访问 /user/123,id = 123
return "user/detail";
}
// 多个路径变量
@GetMapping("/user/{userId}/order/{orderId}")
public String getOrder(@PathVariable Long userId,
@PathVariable Long orderId) {
return "order/detail";
}
// 指定路径变量名
@GetMapping("/user/{id}")
public String getUser(@PathVariable("id") Long userId) {
return "user/detail";
}通配符匹配
// 匹配 /user/任意内容
@GetMapping("/user/*")
public String getUser() {
return "user";
}
// 匹配 /user/任意多级路径
@GetMapping("/user/**")
public String getUserAll() {
return "user";
}6.2 请求参数映射
@RequestParam
// 必需参数
@GetMapping("/user")
public String getUser(@RequestParam String name) {
// 访问 /user?name=Alice
return "user";
}
// 可选参数
@GetMapping("/user")
public String getUser(@RequestParam(required = false) String name) {
return "user";
}
// 默认值
@GetMapping("/user")
public String getUser(@RequestParam(defaultValue = "Guest") String name) {
return "user";
}
// 多个参数
@GetMapping("/user")
public String getUser(@RequestParam String name,
@RequestParam int age) {
// 访问 /user?name=Alice&age=25
return "user";
}参数绑定到对象
public class UserQuery {
private String name;
private Integer age;
// getter/setter...
}
@GetMapping("/user")
public String getUser(UserQuery query) {
// 自动绑定 /user?name=Alice&age=25
return "user";
}6.3 请求头映射
// 要求特定的请求头
@GetMapping(value = "/user", headers = "X-Requested-With=XMLHttpRequest")
public String getUserAjax() {
return "user";
}
// 获取请求头值
@GetMapping("/user")
public String getUser(@RequestHeader("User-Agent") String userAgent) {
return "user";
}6.4 Content-Type映射
// 只接受JSON请求
@PostMapping(value = "/user", consumes = "application/json")
public String createUser(@RequestBody User user) {
return "success";
}
// 返回JSON响应
@GetMapping(value = "/user", produces = "application/json")
@ResponseBody
public User getUser() {
return new User("Alice", 25);
}7. 参数绑定
7.1 基本类型绑定
@GetMapping("/user")
public String getUser(@RequestParam int id,
@RequestParam String name,
@RequestParam boolean active) {
return "user";
}7.2 对象绑定
public class User {
private String name;
private Integer age;
private String email;
// getter/setter...
}
@PostMapping("/user")
public String createUser(User user) {
// 自动绑定表单数据到User对象
return "success";
}7.3 集合绑定
// List绑定
@PostMapping("/user")
public String createUsers(@RequestParam List<String> names) {
// 表单:names=Alice&names=Bob
return "success";
}
// Map绑定
@PostMapping("/user")
public String createUser(@RequestParam Map<String, String> params) {
return "success";
}7.4 日期绑定
@GetMapping("/user")
public String getUser(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthDate) {
return "user";
}7.5 @RequestBody(JSON绑定)
@PostMapping("/user")
public String createUser(@RequestBody User user) {
// 自动将JSON转换为User对象
return "success";
}7.6 文件上传绑定
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
// 处理文件
String fileName = file.getOriginalFilename();
// 保存文件...
}
return "success";
}8. 视图和视图解析器
8.1 JSP视图
配置InternalResourceViewResolver
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>JSP中使用EL表达式
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>User Info</title>
</head>
<body>
<h1>用户信息</h1>
<p>姓名:${user.name}</p>
<p>年龄:${user.age}</p>
</body>
</html>JSP中使用JSTL
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<c:forEach items="${users}" var="user">
<p>${user.name}</p>
</c:forEach>
</body>
</html>8.2 JSON视图
使用@ResponseBody
@GetMapping("/user")
@ResponseBody
public User getUser() {
return new User("Alice", 25);
}使用ResponseEntity
@GetMapping("/user")
public ResponseEntity<User> getUser() {
User user = new User("Alice", 25);
return ResponseEntity.ok(user);
}8.3 重定向和转发
@Controller
public class UserController {
// 转发(默认)
@GetMapping("/user")
public String getUser() {
return "user"; // 转发到user视图
}
// 重定向
@PostMapping("/user")
public String createUser() {
return "redirect:/user/list"; // 重定向到/user/list
}
// 转发到另一个Controller
@GetMapping("/user")
public String getUser() {
return "forward:/other/action"; // 转发到/other/action
}
}9. 数据绑定和验证
9.1 数据绑定
Spring MVC自动将请求参数绑定到方法参数或对象属性。
public class User {
@NotNull
@Size(min = 2, max = 50)
private String name;
@Min(18)
@Max(100)
private Integer age;
@Email
private String email;
// getter/setter...
}9.2 数据验证
添加验证依赖
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.3.Final</version>
</dependency>使用@Valid注解
@PostMapping("/user")
public String createUser(@Valid User user, BindingResult result) {
if (result.hasErrors()) {
return "user/form"; // 返回表单页面
}
// 保存用户
return "redirect:/user/list";
}常用验证注解
@NotNull:不能为null@NotEmpty:不能为空@NotBlank:不能为空白@Size(min=, max=):长度限制@Min、@Max:数值范围@Email:邮箱格式@Pattern:正则表达式@Past、@Future:日期验证
9.3 自定义验证器
public class PhoneValidator implements ConstraintValidator<Phone, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && value.matches("^1[3-9]\\d{9}$");
}
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
String message() default "Invalid phone number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}10. 文件上传和下载
10.1 文件上传
配置MultipartResolver
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/> <!-- 10MB -->
<property name="maxInMemorySize" value="4096"/>
</bean>添加依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>处理文件上传
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
String fileName = file.getOriginalFilename();
// 保存文件
Path path = Paths.get("uploads/" + fileName);
Files.write(path, bytes);
return "redirect:/success";
} catch (IOException e) {
e.printStackTrace();
}
}
return "redirect:/error";
}多文件上传
@PostMapping("/upload")
public String upload(@RequestParam("files") MultipartFile[] files) {
for (MultipartFile file : files) {
if (!file.isEmpty()) {
// 处理每个文件
}
}
return "redirect:/success";
}10.2 文件下载
@GetMapping("/download")
public ResponseEntity<Resource> download(@RequestParam String fileName) {
try {
Path filePath = Paths.get("uploads/" + fileName);
Resource resource = new UrlResource(filePath.toUri());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
} catch (Exception e) {
return ResponseEntity.notFound().build();
}
}11. 拦截器
11.1 创建拦截器
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("Before handling request: " + request.getRequestURI());
return true; // 返回true继续处理,false中断
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("After handling request");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("Request completed");
}
}11.2 配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="com.example.interceptor.LoggingInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>使用Java配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/user/**")
.excludePathPatterns("/user/login");
}
}11.3 实际应用:登录拦截器
public class LoginInterceptor 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;
}
}12. 异常处理
12.1 @ExceptionHandler
@Controller
public class UserController {
@ExceptionHandler(UserNotFoundException.class)
public String handleUserNotFound(UserNotFoundException e, Model model) {
model.addAttribute("error", e.getMessage());
return "error";
}
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
throw new UserNotFoundException("User not found: " + id);
}
return "user";
}
}12.2 @ControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException(Exception e, Model model) {
model.addAttribute("error", e.getMessage());
return "error";
}
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFound(UserNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(e.getMessage());
}
}12.3 @ResponseStatus
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "User not found")
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}13. RESTful API开发
13.1 RESTful设计原则
- 使用HTTP动词(GET、POST、PUT、DELETE)
- 使用名词表示资源
- 使用JSON格式
- 使用HTTP状态码
13.2 RESTful Controller
@RestController // 等价于@Controller + @ResponseBody
@RequestMapping("/api/users")
public class UserRestController {
@Autowired
private UserService userService;
// GET /api/users - 获取所有用户
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
// GET /api/users/{id} - 获取单个用户
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
// POST /api/users - 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User saved = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
}
// PUT /api/users/{id} - 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id,
@RequestBody User user) {
User updated = userService.update(id, user);
return ResponseEntity.ok(updated);
}
// DELETE /api/users/{id} - 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}13.3 内容协商
@GetMapping(value = "/user/{id}", produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}14. Spring MVC与Spring Boot集成
14.1 创建Spring Boot项目
使用Spring Initializr创建项目,选择:
- Spring Web
- Thymeleaf(可选)
14.2 配置Spring MVC
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
}
}14.3 application.properties配置
# 视图配置
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
# 静态资源
spring.web.resources.static-locations=classpath:/static/
spring.web.resources.cache.period=3600
# 文件上传
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB15. 最佳实践
15.1 Controller设计原则
- 单一职责:每个Controller只处理相关请求
- 使用@RestController:RESTful API使用@RestController
- 合理分层:Controller只处理请求,业务逻辑在Service层
- 统一异常处理:使用@ControllerAdvice统一处理异常
15.2 URL设计
- 使用RESTful风格
- 使用名词表示资源
- 避免动词在URL中
- 使用层级结构
15.3 性能优化
- 使用缓存:合理使用缓存
- 异步处理:耗时操作使用异步
- 静态资源:正确配置静态资源
- 数据库优化:避免N+1查询
15.4 安全考虑
- 输入验证:验证所有用户输入
- XSS防护:防止跨站脚本攻击
- CSRF防护:防止跨站请求伪造
- 权限控制:实现权限验证
16. 总结与进阶
16.1 Spring MVC核心要点
- ✅ DispatcherServlet是核心
- ✅ 注解驱动开发
- ✅ 灵活的请求映射
- ✅ 强大的数据绑定
- ✅ 多种视图支持
16.2 学习路径
- 基础:掌握基本概念和使用
- 进阶:深入学习原理和源码
- 实践:通过项目实践巩固
- 优化:学习性能优化技巧
16.3 推荐资源
- 官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
- Spring Boot:学习Spring Boot简化配置
- 源码阅读:阅读Spring MVC源码
16.4 常见问题
Q: Spring MVC和Spring Boot的关系?
A: Spring Boot基于Spring MVC,提供了自动配置,简化了开发。
Q: 如何选择视图技术?
A: JSP适合传统项目,Thymeleaf适合现代Web应用,前后端分离使用JSON。
Q: 如何实现前后端分离?
A: 使用@RestController返回JSON,前端使用Ajax调用。
结语
Spring MVC是一个功能强大、灵活的Web框架。通过本教程的学习,相信你已经掌握了Spring MVC的核心功能和使用方法。
记住:
- 多实践:理论结合实践,多写代码
- 理解原理:理解Spring MVC的工作原理
- 关注最佳实践:遵循最佳实践编写代码
- 持续学习:关注Spring生态的发展
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。