Spring MVC详解:从原理到实战
前言
Spring MVC是Spring Framework的一个核心模块,是目前最流行的Java Web框架之一。它基于MVC设计模式,提供了一套完整的Web应用开发解决方案。本文将深入讲解Spring MVC的核心原理、关键组件和实战应用。
一、Spring MVC概述
1.1 MVC架构
MVC(Model-View-Controller)是一种软件架构模式,将应用分为三个核心组件:
markdown
MVC架构图
┌─────────────────────────────────────────┐
│ 浏览器(Browser) │
└──────────────┬──────────────────────────┘
│ HTTP Request
▼
┌─────────────────────────────────────────┐
│ Controller(控制器) │
│ - 接收请求 │
│ - 调用业务逻辑 │
│ - 返回视图名称 │
└──────┬──────────────────────┬───────────┘
│ │
│ 调用 │ 返回Model
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ Model │ │ View │
│ (模型) │ │ (视图) │
│ - 业务数据 │ │ - 渲染页面 │
│ - 业务逻辑 │ │ - 展示数据 │
└──────────────┘ └──────────────────┘
1.2 Spring MVC核心组件
markdown
Spring MVC请求处理流程
┌─────────────────────────────────────────┐
│ 1. DispatcherServlet │
│ - 前端控制器,统一处理请求 │
└────────────┬────────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 2. HandlerMapping │
│ - 根据URL查找Handler │
└────────────┬────────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 3. HandlerAdapter │
│ - 执行Handler(Controller方法) │
└────────────┬────────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 4. Controller │
│ - 处理业务逻辑 │
└────────────┬────────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 5. ViewResolver │
│ - 解析视图名称为具体视图 │
└────────────┬────────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 6. View │
│ - 渲染并返回响应 │
└─────────────────────────────────────────┘
二、快速开始
2.1 Maven依赖
xml
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf模板引擎(可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.2 第一个Controller
java
/**
* Hello World Controller
*/
@RestController
@RequestMapping("/api")
public class HelloController {
/**
* 简单的GET请求
*/
@GetMapping("/hello")
public String hello() {
return "Hello, Spring MVC!";
}
/**
* 路径参数
*/
@GetMapping("/hello/{name}")
public String helloWithName(@PathVariable String name) {
return "Hello, " + name + "!";
}
/**
* 请求参数
*/
@GetMapping("/greet")
public String greet(@RequestParam String name,
@RequestParam(defaultValue = "18") Integer age) {
return String.format("Hello, %s! You are %d years old.", name, age);
}
/**
* 返回JSON对象
*/
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return new User(id, "张三", "zhangsan@example.com");
}
}
@Data
@AllArgsConstructor
class User {
private Long id;
private String name;
private String email;
}
2.3 配置类
java
/**
* Web MVC配置
*/
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 配置静态资源处理
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
/**
* 配置视图解析器
*/
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
/**
* 配置消息转换器
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
}
/**
* 配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**");
}
/**
* 跨域配置
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
三、请求处理
3.1 请求映射注解
java
/**
* 请求映射注解详解
*/
@RestController
@RequestMapping("/api/products")
public class ProductController {
/**
* @GetMapping - 处理GET请求
*/
@GetMapping
public List<Product> getAllProducts() {
return Arrays.asList(
new Product(1L, "商品1", new BigDecimal("99.99")),
new Product(2L, "商品2", new BigDecimal("199.99"))
);
}
/**
* @PostMapping - 处理POST请求
*/
@PostMapping
public Product createProduct(@RequestBody Product product) {
product.setId(System.currentTimeMillis());
return product;
}
/**
* @PutMapping - 处理PUT请求
*/
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id,
@RequestBody Product product) {
product.setId(id);
return product;
}
/**
* @DeleteMapping - 处理DELETE请求
*/
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
System.out.println("删除商品: " + id);
}
/**
* @PatchMapping - 处理PATCH请求
*/
@PatchMapping("/{id}")
public Product patchProduct(@PathVariable Long id,
@RequestBody Map<String, Object> updates) {
Product product = new Product(id, "商品" + id, new BigDecimal("99.99"));
// 应用部分更新
return product;
}
/**
* 多个HTTP方法
*/
@RequestMapping(value = "/{id}/status",
method = {RequestMethod.GET, RequestMethod.POST})
public String getStatus(@PathVariable Long id) {
return "商品状态";
}
/**
* 请求参数条件匹配
*/
@GetMapping(params = "category=electronics")
public List<Product> getElectronics() {
return new ArrayList<>();
}
/**
* 请求头条件匹配
*/
@GetMapping(headers = "X-API-Version=1")
public List<Product> getProductsV1() {
return new ArrayList<>();
}
/**
* Content-Type条件匹配
*/
@PostMapping(consumes = "application/json")
public Product createProductJson(@RequestBody Product product) {
return product;
}
/**
* Accept条件匹配
*/
@GetMapping(value = "/{id}", produces = "application/json")
public Product getProductJson(@PathVariable Long id) {
return new Product(id, "商品" + id, new BigDecimal("99.99"));
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Product {
private Long id;
private String name;
private BigDecimal price;
}
3.2 参数绑定
java
/**
* 参数绑定详解
*/
@RestController
@RequestMapping("/api/demo")
public class ParameterBindingController {
/**
* @PathVariable - 路径变量
*/
@GetMapping("/users/{userId}/orders/{orderId}")
public String getOrder(@PathVariable Long userId,
@PathVariable("orderId") Long id) {
return "用户" + userId + "的订单" + id;
}
/**
* @RequestParam - 请求参数
*/
@GetMapping("/search")
public String search(
@RequestParam String keyword, // 必需参数
@RequestParam(required = false) String category, // 可选参数
@RequestParam(defaultValue = "1") Integer page, // 默认值
@RequestParam(name = "size") Integer pageSize // 参数重命名
) {
return String.format("搜索: %s, 分类: %s, 页码: %d, 大小: %d",
keyword, category, page, pageSize);
}
/**
* @RequestBody - 请求体
*/
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return user;
}
/**
* @RequestHeader - 请求头
*/
@GetMapping("/info")
public String getInfo(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value = "Accept-Language", defaultValue = "zh-CN") String language
) {
return "浏览器: " + userAgent + ", 语言: " + language;
}
/**
* @CookieValue - Cookie值
*/
@GetMapping("/session")
public String getSession(@CookieValue("JSESSIONID") String sessionId) {
return "会话ID: " + sessionId;
}
/**
* @ModelAttribute - 模型属性
*/
@PostMapping("/form")
public User submitForm(@ModelAttribute User user) {
return user;
}
/**
* HttpServletRequest - 原生请求对象
*/
@GetMapping("/request")
public String handleRequest(HttpServletRequest request) {
String method = request.getMethod();
String uri = request.getRequestURI();
return method + " " + uri;
}
/**
* 数组参数
*/
@GetMapping("/batch")
public String batchDelete(@RequestParam Long[] ids) {
return "删除: " + Arrays.toString(ids);
}
/**
* List参数
*/
@GetMapping("/list")
public String getList(@RequestParam List<String> tags) {
return "标签: " + tags;
}
/**
* Map参数
*/
@GetMapping("/map")
public String getMap(@RequestParam Map<String, String> params) {
return "参数: " + params;
}
/**
* 自定义对象绑定
*/
@GetMapping("/query")
public String query(SearchQuery query) {
return "查询: " + query;
}
}
@Data
class SearchQuery {
private String keyword;
private String category;
private Integer page;
private Integer size;
}
3.3 参数校验
java
/**
* 参数校验
*/
@RestController
@RequestMapping("/api/validation")
@Validated
public class ValidationController {
/**
* 实体校验
*/
@PostMapping("/users")
public User createUser(@Valid @RequestBody UserDTO userDTO) {
return new User(null, userDTO.getUsername(), userDTO.getEmail());
}
/**
* 参数校验
*/
@GetMapping("/users/{id}")
public User getUser(@PathVariable @Min(1) Long id) {
return new User(id, "User" + id, "user@example.com");
}
/**
* 分组校验
*/
@PostMapping("/users/advanced")
public User createUserAdvanced(
@Validated(CreateGroup.class) @RequestBody UserDTO userDTO
) {
return new User(null, userDTO.getUsername(), userDTO.getEmail());
}
/**
* 异常处理
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidationException(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
}
/**
* 校验DTO
*/
@Data
class UserDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;
@NotBlank(message = "密码不能为空")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$",
message = "密码至少8位,包含大小写字母和数字")
private String password;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 18, message = "年龄必须大于等于18")
@Max(value = 100, message = "年龄必须小于等于100")
private Integer age;
@NotNull(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
}
/**
* 校验分组
*/
interface CreateGroup {}
interface UpdateGroup {}
四、响应处理
4.1 返回值类型
java
/**
* 响应处理
*/
@RestController
@RequestMapping("/api/response")
public class ResponseController {
/**
* 返回String(直接输出)
*/
@GetMapping("/string")
public String returnString() {
return "Hello World";
}
/**
* 返回对象(自动转JSON)
*/
@GetMapping("/object")
public User returnObject() {
return new User(1L, "张三", "zhangsan@example.com");
}
/**
* 返回List
*/
@GetMapping("/list")
public List<User> returnList() {
return Arrays.asList(
new User(1L, "张三", "zhangsan@example.com"),
new User(2L, "李四", "lisi@example.com")
);
}
/**
* 返回Map
*/
@GetMapping("/map")
public Map<String, Object> returnMap() {
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "success");
result.put("data", new User(1L, "张三", "zhangsan@example.com"));
return result;
}
/**
* 返回ResponseEntity(自定义状态码和响应头)
*/
@GetMapping("/entity")
public ResponseEntity<User> returnEntity() {
User user = new User(1L, "张三", "zhangsan@example.com");
return ResponseEntity
.ok()
.header("X-Custom-Header", "value")
.body(user);
}
/**
* 返回不同状态码
*/
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
user.setId(System.currentTimeMillis());
return ResponseEntity
.status(HttpStatus.CREATED)
.body(user);
}
/**
* 返回空响应
*/
@DeleteMapping("/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
return ResponseEntity.noContent().build();
}
/**
* 下载文件
*/
@GetMapping("/download")
public ResponseEntity<Resource> downloadFile() {
ByteArrayResource resource = new ByteArrayResource("文件内容".getBytes());
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"file.txt\"")
.body(resource);
}
/**
* 重定向
*/
@GetMapping("/redirect")
public String redirect() {
return "redirect:/api/response/string";
}
/**
* 转发
*/
@GetMapping("/forward")
public String forward() {
return "forward:/api/response/object";
}
}
4.2 统一响应格式
java
/**
* 统一响应结果
*/
@Data
@AllArgsConstructor
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, "成功", data);
}
public static <T> Result<T> error(String message) {
return new Result<>(500, message, null);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
}
/**
* 响应包装Advice
*/
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
// 判断是否需要包装
return !returnType.getParameterType().equals(Result.class);
}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
// 如果已经是Result类型,直接返回
if (body instanceof Result) {
return body;
}
// 包装为Result
return Result.success(body);
}
}
五、拦截器与过滤器
5.1 拦截器
java
/**
* 日志拦截器
*/
@Slf4j
public class LoggingInterceptor implements HandlerInterceptor {
/**
* 请求处理前
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String method = request.getMethod();
String uri = request.getRequestURI();
log.info("请求开始: {} {}", method, uri);
// 记录开始时间
request.setAttribute("startTime", System.currentTimeMillis());
return true; // 返回true继续处理,false中断
}
/**
* 请求处理后,视图渲染前
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
log.info("请求处理完成");
}
/**
* 请求完成后
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
Long startTime = (Long) request.getAttribute("startTime");
long duration = System.currentTimeMillis() - startTime;
log.info("请求结束,耗时: {}ms", duration);
if (ex != null) {
log.error("请求异常", ex);
}
}
}
/**
* 认证拦截器
*/
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 获取Token
String token = request.getHeader("Authorization");
if (token == null || !validateToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("未授权");
return false;
}
return true;
}
private boolean validateToken(String token) {
// 验证Token
return true;
}
}
5.2 过滤器
java
/**
* 自定义过滤器
*/
@Component
@Order(1)
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化");
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
System.out.println("过滤器前置处理: " + req.getRequestURI());
// 继续过滤器链
chain.doFilter(request, response);
System.out.println("过滤器后置处理");
}
@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}
/**
* 使用FilterRegistrationBean注册过滤器
*/
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<CustomFilter> customFilter() {
FilterRegistrationBean<CustomFilter> registration =
new FilterRegistrationBean<>();
registration.setFilter(new CustomFilter());
registration.addUrlPatterns("/api/*");
registration.setOrder(1);
return registration;
}
}
5.3 拦截器vs过滤器
css
拦截器 vs 过滤器对比
┌──────────────┬───────────────┬──────────────┐
│ 特性 │ 拦截器 │ 过滤器 │
├──────────────┼───────────────┼──────────────┤
│ 实现原理 │ 动态代理 │ 函数回调 │
│ 规范 │ Spring规范 │ Servlet规范 │
│ 依赖 │ 依赖Spring │ 不依赖Spring │
│ 拦截范围 │ 只拦截Controller│ 拦截所有请求│
│ 访问Spring容器│ 可以 │ 不可以 │
│ 执行顺序 │ 在Filter之后 │ 在Interceptor之前│
└──────────────┴───────────────┴──────────────┘
六、异常处理
6.1 全局异常处理
java
/**
* 全局异常处理器
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理参数校验异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> handleValidationException(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return Result.error(400, "参数校验失败:" + errors);
}
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusinessException(BusinessException ex) {
log.error("业务异常: {}", ex.getMessage());
return Result.error(ex.getCode(), ex.getMessage());
}
/**
* 处理资源未找到异常
*/
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Result<?> handleResourceNotFoundException(ResourceNotFoundException ex) {
return Result.error(404, ex.getMessage());
}
/**
* 处理非法参数异常
*/
@ExceptionHandler(IllegalArgumentException.class)
public Result<?> handleIllegalArgumentException(IllegalArgumentException ex) {
return Result.error(400, "非法参数:" + ex.getMessage());
}
/**
* 处理SQL异常
*/
@ExceptionHandler(SQLException.class)
public Result<?> handleSQLException(SQLException ex) {
log.error("数据库异常", ex);
return Result.error(500, "数据库错误");
}
/**
* 处理所有未捕获的异常
*/
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception ex) {
log.error("系统异常", ex);
return Result.error(500, "系统错误:" + ex.getMessage());
}
}
/**
* 自定义业务异常
*/
@Data
public class BusinessException extends RuntimeException {
private Integer code;
public BusinessException(String message) {
super(message);
this.code = 500;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
}
/**
* 资源未找到异常
*/
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
七、文件上传下载
7.1 文件上传
java
/**
* 文件上传Controller
*/
@RestController
@RequestMapping("/api/files")
public class FileUploadController {
@Value("${upload.path:/tmp/uploads}")
private String uploadPath;
/**
* 单文件上传
*/
@PostMapping("/upload")
public Result<String> uploadFile(@RequestParam("file") MultipartFile file) {
try {
if (file.isEmpty()) {
return Result.error("文件不能为空");
}
// 生成文件名
String originalFilename = file.getOriginalFilename();
String extension = originalFilename.substring(
originalFilename.lastIndexOf("."));
String filename = UUID.randomUUID().toString() + extension;
// 保存文件
Path path = Paths.get(uploadPath, filename);
Files.createDirectories(path.getParent());
file.transferTo(path.toFile());
return Result.success(filename);
} catch (IOException e) {
return Result.error("文件上传失败:" + e.getMessage());
}
}
/**
* 多文件上传
*/
@PostMapping("/batch-upload")
public Result<List<String>> uploadFiles(
@RequestParam("files") MultipartFile[] files) {
List<String> filenames = new ArrayList<>();
for (MultipartFile file : files) {
if (!file.isEmpty()) {
// 保存文件逻辑
String filename = saveFile(file);
filenames.add(filename);
}
}
return Result.success(filenames);
}
/**
* 带参数的文件上传
*/
@PostMapping("/upload-with-data")
public Result<FileUploadResponse> uploadWithData(
@RequestParam("file") MultipartFile file,
@RequestParam("title") String title,
@RequestParam("description") String description) {
String filename = saveFile(file);
FileUploadResponse response = new FileUploadResponse();
response.setFilename(filename);
response.setTitle(title);
response.setDescription(description);
response.setSize(file.getSize());
return Result.success(response);
}
private String saveFile(MultipartFile file) {
try {
String originalFilename = file.getOriginalFilename();
String extension = originalFilename.substring(
originalFilename.lastIndexOf("."));
String filename = UUID.randomUUID().toString() + extension;
Path path = Paths.get(uploadPath, filename);
Files.createDirectories(path.getParent());
file.transferTo(path.toFile());
return filename;
} catch (IOException e) {
throw new RuntimeException("文件保存失败", e);
}
}
}
@Data
class FileUploadResponse {
private String filename;
private String title;
private String description;
private Long size;
}
7.2 文件下载
java
/**
* 文件下载Controller
*/
@RestController
@RequestMapping("/api/download")
public class FileDownloadController {
@Value("${upload.path:/tmp/uploads}")
private String uploadPath;
/**
* 文件下载
*/
@GetMapping("/{filename}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
try {
Path path = Paths.get(uploadPath, filename);
Resource resource = new UrlResource(path.toUri());
if (!resource.exists()) {
throw new ResourceNotFoundException("文件不存在: " + filename);
}
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"")
.body(resource);
} catch (IOException e) {
throw new RuntimeException("文件下载失败", e);
}
}
/**
* 在线预览
*/
@GetMapping("/preview/{filename}")
public ResponseEntity<Resource> previewFile(@PathVariable String filename) {
try {
Path path = Paths.get(uploadPath, filename);
Resource resource = new UrlResource(path.toUri());
if (!resource.exists()) {
throw new ResourceNotFoundException("文件不存在");
}
// 根据文件类型设置Content-Type
String contentType = Files.probeContentType(path);
if (contentType == null) {
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.body(resource);
} catch (IOException e) {
throw new RuntimeException("文件预览失败", e);
}
}
}
八、实战案例
8.1 案例1:RESTful API设计
java
/**
* RESTful风格的用户管理API
*/
@RestController
@RequestMapping("/api/users")
@Slf4j
public class UserRestController {
@Autowired
private UserService userService;
/**
* 获取用户列表
* GET /api/users?page=1&size=10&keyword=zhang
*/
@GetMapping
public Result<Page<User>> getUserList(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) String keyword) {
Page<User> users = userService.findUsers(page, size, keyword);
return Result.success(users);
}
/**
* 获取单个用户
* GET /api/users/1
*/
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
throw new ResourceNotFoundException("用户不存在: " + id);
}
return Result.success(user);
}
/**
* 创建用户
* POST /api/users
*/
@PostMapping
public Result<User> createUser(@Valid @RequestBody UserDTO userDTO) {
User user = userService.createUser(userDTO);
return Result.success(user);
}
/**
* 更新用户
* PUT /api/users/1
*/
@PutMapping("/{id}")
public Result<User> updateUser(
@PathVariable Long id,
@Valid @RequestBody UserDTO userDTO) {
User user = userService.updateUser(id, userDTO);
return Result.success(user);
}
/**
* 部分更新用户
* PATCH /api/users/1
*/
@PatchMapping("/{id}")
public Result<User> patchUser(
@PathVariable Long id,
@RequestBody Map<String, Object> updates) {
User user = userService.patchUser(id, updates);
return Result.success(user);
}
/**
* 删除用户
* DELETE /api/users/1
*/
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return Result.success(null);
}
/**
* 批量删除
* DELETE /api/users?ids=1,2,3
*/
@DeleteMapping
public Result<Void> batchDelete(@RequestParam List<Long> ids) {
userService.batchDelete(ids);
return Result.success(null);
}
}
@Data
class Page<T> {
private Long total;
private Integer page;
private Integer size;
private List<T> records;
}
8.2 案例2:异步请求处理
java
/**
* 异步请求处理
*/
@RestController
@RequestMapping("/api/async")
public class AsyncController {
@Autowired
private AsyncService asyncService;
/**
* Callable异步处理
*/
@GetMapping("/callable")
public Callable<String> handleCallable() {
return () -> {
Thread.sleep(2000); // 模拟耗时操作
return "Callable结果";
};
}
/**
* DeferredResult异步处理
*/
@GetMapping("/deferred")
public DeferredResult<String> handleDeferred() {
DeferredResult<String> result = new DeferredResult<>(5000L);
// 异步处理
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(2000);
result.setResult("DeferredResult结果");
} catch (InterruptedException e) {
result.setErrorResult("处理失败");
}
});
return result;
}
/**
* CompletableFuture异步处理
*/
@GetMapping("/future")
public CompletableFuture<String> handleFuture() {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
return "CompletableFuture结果";
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
/**
* SSE(Server-Sent Events)
*/
@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handleSse() {
SseEmitter emitter = new SseEmitter();
CompletableFuture.runAsync(() -> {
try {
for (int i = 0; i < 10; i++) {
emitter.send("数据 " + i);
Thread.sleep(1000);
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
});
return emitter;
}
}
8.3 案例3:内容协商
java
/**
* 内容协商(根据Accept返回不同格式)
*/
@Controller
@RequestMapping("/api/content")
public class ContentNegotiationController {
/**
* 返回JSON或XML
*/
@GetMapping(value = "/user/{id}",
produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
@ResponseBody
public User getUser(@PathVariable Long id) {
return new User(id, "张三", "zhangsan@example.com");
}
/**
* 返回HTML或JSON
*/
@GetMapping("/users")
public Object getUsers(@RequestHeader(value = "Accept",
defaultValue = "text/html") String accept) {
List<User> users = Arrays.asList(
new User(1L, "张三", "zhangsan@example.com"),
new User(2L, "李四", "lisi@example.com")
);
if (accept.contains("application/json")) {
return new ResponseEntity<>(users, HttpStatus.OK);
} else {
return "users"; // 返回视图名称
}
}
}
九、最佳实践
9.1 RESTful设计原则
sql
RESTful API设计规范
┌─────────────────────────────────────────┐
│ 1. 使用名词而非动词 │
│ ✓ GET /users │
│ ✗ GET /getUsers │
├─────────────────────────────────────────┤
│ 2. 使用复数形式 │
│ ✓ GET /users/1 │
│ ✗ GET /user/1 │
├─────────────────────────────────────────┤
│ 3. 使用HTTP方法表示操作 │
│ GET - 查询 │
│ POST - 创建 │
│ PUT - 完整更新 │
│ PATCH - 部分更新 │
│ DELETE - 删除 │
├─────────────────────────────────────────┤
│ 4. 使用HTTP状态码 │
│ 200 - 成功 │
│ 201 - 创建成功 │
│ 204 - 无内容 │
│ 400 - 客户端错误 │
│ 401 - 未授权 │
│ 404 - 未找到 │
│ 500 - 服务器错误 │
├─────────────────────────────────────────┤
│ 5. 版本控制 │
│ /api/v1/users │
│ 或使用请求头: Accept-Version: v1 │
└─────────────────────────────────────────┘
9.2 性能优化
java
/**
* 性能优化配置
*/
@Configuration
public class PerformanceConfig {
/**
* 配置HTTP缓存
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(7, TimeUnit.DAYS));
}
/**
* 配置Gzip压缩
*/
@Bean
public FilterRegistrationBean<CommonsRequestLoggingFilter> gzipFilter() {
// Spring Boot会自动配置Gzip
return null;
}
}
十、总结
核心知识点回顾
sql
Spring MVC核心要点
│
├── 核心组件
│ ├── DispatcherServlet(前端控制器)
│ ├── HandlerMapping(处理器映射)
│ ├── Controller(控制器)
│ └── ViewResolver(视图解析器)
│
├── 请求处理
│ ├── @RequestMapping系列注解
│ ├── 参数绑定(@PathVariable/@RequestParam等)
│ ├── 参数校验(@Valid/@Validated)
│ └── 请求体处理(@RequestBody)
│
├── 响应处理
│ ├── 返回值类型
│ ├── ResponseEntity
│ ├── 统一响应格式
│ └── 内容协商
│
├── 拦截与过滤
│ ├── 拦截器(HandlerInterceptor)
│ └── 过滤器(Filter)
│
├── 异常处理
│ ├── @ExceptionHandler
│ ├── @ControllerAdvice
│ └── 全局异常处理
│
└── 高级特性
├── 文件上传下载
├── 异步请求处理
├── RESTful API
└── 跨域配置
Spring MVC是构建Web应用的强大框架,掌握其核心原理和最佳实践,能够帮助我们开发出高质量的Web应用。