SpringMVC请求映射:@RequestMapping的高级用法
文章目录
- 引言
- 一、路径模式与变量提取
- 二、请求条件组合与精确控制
- 三、请求映射注解变体与语义化
- 四、URI模板与矩阵变量支持
- 五、自定义请求映射与注解组合
- 六、请求映射最佳实践与性能优化
- 总结
引言
在SpringMVC框架中,请求映射是连接客户端请求与服务器处理逻辑的桥梁。@RequestMapping注解作为SpringMVC最核心的注解之一,为开发者提供了强大而灵活的URL映射机制。随着Web应用复杂度的提高,简单的URL匹配已不能满足现代应用开发的需求。深入理解@RequestMapping的高级特性和用法,对于构建健壮、灵活的Web应用至关重要。本文将深入探讨@RequestMapping的高级用法,包括复杂路径匹配、多维度请求条件组合、注解变体的应用场景以及RESTful API的最佳实践,帮助开发者更加高效地使用这一强大工具,构建出更加优雅的Web应用。
一、路径模式与变量提取
@RequestMapping支持多种路径匹配模式,从简单的精确匹配到复杂的通配符和路径变量。路径变量使用{varName}语法定义,可以通过@PathVariable注解将其绑定到方法参数。SpringMVC还支持正则表达式路径变量{varName:regex},用于更精确的匹配控制。对于层次化的资源路径,可以使用**通配符捕获多级路径。这些高级匹配特性使得URL设计更加灵活,能够优雅地表达资源之间的层次关系,同时通过路径变量直观地传递参数,符合RESTful设计理念。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 演示@RequestMapping的高级路径匹配
*/
@Controller
@RequestMapping("/api")
public class AdvancedPathMappingController {
/**
* 基本路径变量
* URL示例: /api/users/123
*/
@RequestMapping("/users/{userId}")
@ResponseBody
public String getUserById(@PathVariable("userId") Long userId) {
return "User ID: " + userId;
}
/**
* 多个路径变量
* URL示例: /api/users/123/orders/456
*/
@RequestMapping("/users/{userId}/orders/{orderId}")
@ResponseBody
public String getUserOrder(
@PathVariable("userId") Long userId,
@PathVariable("orderId") Long orderId) {
return "User ID: " + userId + ", Order ID: " + orderId;
}
/**
* 使用正则表达式限制路径变量
* 仅匹配5位数字的产品代码
* URL示例: /api/products/12345
*/
@RequestMapping("/products/{productCode:[0-9]{5}}")
@ResponseBody
public String getProductByCode(@PathVariable("productCode") String productCode) {
return "Product code: " + productCode;
}
/**
* 通配符路径匹配
* 匹配如:/api/files/documents/report.pdf
*/
@RequestMapping("/files/**")
@ResponseBody
public String getFile() {
return "File path: " + request.getRequestURI();
}
/**
* 路径变量与通配符结合
* 匹配如:/api/repos/myorg/myrepo/tree/master/src/main
*/
@RequestMapping("/repos/{org}/{repo}/tree/{branch}/**")
@ResponseBody
public String getRepositoryContent(
@PathVariable("org") String organization,
@PathVariable("repo") String repository,
@PathVariable("branch") String branch) {
String fullPath = request.getRequestURI();
String relativePath = fullPath.substring(fullPath.indexOf("/tree/" + branch) +
("/tree/" + branch).length());
return "Organization: " + organization +
", Repository: " + repository +
", Branch: " + branch +
", Path: " + relativePath;
}
/**
* 可选路径变量(通过正则表达式实现)
* 匹配:/api/search 和 /api/search/query
*/
@RequestMapping("/search{/query:.*}")
@ResponseBody
public String search(@PathVariable(value = "query", required = false) String query) {
if (query == null || query.isEmpty()) {
return "Search home page";
}
return "Search results for: " + query.substring(1); // 去除开头的斜杠
}
}
二、请求条件组合与精确控制
@RequestMapping不仅可以基于URL路径进行匹配,还支持多种请求条件的组合,包括HTTP方法(method)、请求参数(params)、请求头(headers)、媒体类型(consumes/produces)等。通过组合这些条件,可以实现对请求的精确筛选和控制。对于复杂的Web应用,不同的客户端(如浏览器、移动应用、API客户端)可能需要不同的处理逻辑,通过请求条件组合可以优雅地解决这一问题。此外,基于Content-Type的映射对于构建支持多种数据格式的API尤为重要,可以根据客户端期望的数据格式提供相应的处理逻辑。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 演示@RequestMapping的请求条件组合
*/
@Controller
@RequestMapping("/products")
public class RequestConditionController {
/**
* 基于HTTP方法的映射(不使用便捷注解)
* 仅处理GET请求
*/
@RequestMapping(value = "", method = RequestMethod.GET)
@ResponseBody
public String listProducts() {
return "Product list";
}
/**
* 基于请求参数的映射
* 仅当请求包含'category'参数且值为'books'时匹配
* URL示例: /products/filter?category=books
*/
@RequestMapping(value = "/filter", params = "category=books")
@ResponseBody
public String filterBookProducts() {
return "Filtered book products";
}
/**
* 带有多个请求参数条件
* 要求有sort参数,但没有order参数
* URL示例: /products/filter?category=electronics&sort=price
*/
@RequestMapping(
value = "/filter",
params = {"category=electronics", "sort", "!order"}
)
@ResponseBody
public String filterElectronicsProductsSorted() {
return "Filtered electronics products, sorted";
}
/**
* 基于请求头的映射
* 仅匹配来自移动设备的请求(通过User-Agent判断)
*/
@RequestMapping(
value = "/view",
headers = "User-Agent=Mozilla/5.0.*Android.*"
)
@ResponseBody
public String viewProductOnMobile() {
return "Mobile view of product";
}
/**
* 处理来自特定客户端的请求
* 基于自定义请求头进行匹配
*/
@RequestMapping(
value = "/view",
headers = "X-API-VERSION=1"
)
@ResponseBody
public String viewProductV1API() {
return "Product view API v1";
}
/**
* 基于Accept头的映射(produces属性)
* 客户端期望接收HTML响应
*/
@RequestMapping(
value = "/details/{id}",
produces = "text/html"
)
public String getProductDetailsHtml() {
return "product/details"; // 返回视图名称
}
/**
* 基于Accept头的映射(produces属性)
* 客户端期望接收JSON响应
*/
@RequestMapping(
value = "/details/{id}",
produces = "application/json"
)
@ResponseBody
public Product getProductDetailsJson(@PathVariable Long id) {
Product product = productService.findById(id);
return product; // 返回JSON数据
}
/**
* 基于Content-Type头的映射(consumes属性)
* 处理JSON格式的请求体
*/
@RequestMapping(
value = "",
method = RequestMethod.POST,
consumes = "application/json"
)
@ResponseBody
public String createProductFromJson(@RequestBody Product product) {
productService.save(product);
return "Product created from JSON";
}
/**
* 基于Content-Type头的映射(consumes属性)
* 处理XML格式的请求体
*/
@RequestMapping(
value = "",
method = RequestMethod.POST,
consumes = "application/xml"
)
@ResponseBody
public String createProductFromXml(@RequestBody Product product) {
productService.save(product);
return "Product created from XML";
}
/**
* 复杂条件组合示例
* 同时指定HTTP方法、请求参数、请求头、Content-Type和Accept
*/
@RequestMapping(
value = "/process",
method = RequestMethod.PUT,
params = "action=update",
headers = "X-API-TOKEN",
consumes = "application/json",
produces = "application/json"
)
@ResponseBody
public Product processProductUpdate(@RequestBody Product product) {
return productService.update(product);
}
}
三、请求映射注解变体与语义化
从Spring 4.3开始,框架引入了一系列@RequestMapping的变体注解,包括@GetMapping、@PostMapping、@PutMapping、@DeleteMapping和@PatchMapping,用于简化特定HTTP方法的映射定义。这些变体注解不仅简化了代码,还提高了代码的语义化,使API意图更加明确。在RESTful API开发中,选择合适的HTTP方法对于表达资源操作的语义至关重要。@GetMapping用于资源获取,@PostMapping用于资源创建,@PutMapping用于资源完整更新,@PatchMapping用于资源部分更新,@DeleteMapping用于资源删除。通过使用这些语义化注解,可以构建出符合REST原则的API。
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
/**
* 使用@RequestMapping变体注解构建RESTful API
*/
@RestController
@RequestMapping("/api/articles")
public class ArticleApiController {
private final ArticleService articleService;
public ArticleApiController(ArticleService articleService) {
this.articleService = articleService;
}
/**
* 获取文章列表
* 使用@GetMapping替代@RequestMapping(method = RequestMethod.GET)
*/
@GetMapping
public List<Article> getAllArticles() {
return articleService.findAll();
}
/**
* 获取特定文章
* @PathVariable直接注解在路径参数上
*/
@GetMapping("/{id}")
public ResponseEntity<Article> getArticleById(@PathVariable Long id) {
return articleService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 创建新文章
* 使用@PostMapping替代@RequestMapping(method = RequestMethod.POST)
*/
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Article createArticle(@Valid @RequestBody Article article) {
return articleService.create(article);
}
/**
* 完全更新文章
* 使用@PutMapping替代@RequestMapping(method = RequestMethod.PUT)
*/
@PutMapping("/{id}")
public ResponseEntity<Article> updateArticle(
@PathVariable Long id,
@Valid @RequestBody Article article) {
return articleService.update(id, article)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 部分更新文章
* 使用@PatchMapping替代@RequestMapping(method = RequestMethod.PATCH)
*/
@PatchMapping("/{id}")
public ResponseEntity<Article> partialUpdateArticle(
@PathVariable Long id,
@RequestBody Map<String, Object> updates) {
return articleService.partialUpdate(id, updates)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 删除文章
* 使用@DeleteMapping替代@RequestMapping(method = RequestMethod.DELETE)
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteArticle(@PathVariable Long id) {
boolean deleted = articleService.delete(id);
if (deleted) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
}
/**
* 嵌套资源:获取文章评论
* 展示如何处理资源之间的关系
*/
@GetMapping("/{articleId}/comments")
public List<Comment> getArticleComments(@PathVariable Long articleId) {
return commentService.findByArticleId(articleId);
}
/**
* 嵌套资源:添加文章评论
*/
@PostMapping("/{articleId}/comments")
@ResponseStatus(HttpStatus.CREATED)
public Comment addArticleComment(
@PathVariable Long articleId,
@Valid @RequestBody Comment comment) {
comment.setArticleId(articleId);
return commentService.create(comment);
}
/**
* 使用请求参数过滤资源
* 展示如何与@RequestParam结合使用
*/
@GetMapping("/search")
public List<Article> searchArticles(
@RequestParam(required = false) String title,
@RequestParam(required = false) String author,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return articleService.search(title, author, page, size);
}
}
四、URI模板与矩阵变量支持
SpringMVC支持更高级的URI模板特性,如矩阵变量(Matrix Variables)。矩阵变量是URI路径中的键值对,使用分号(;)分隔,形如/path;param1=value1;param2=value2。这一特性在RESTful API中很有用,尤其是需要在路径中传递多个可选参数时。与查询参数不同,矩阵变量是路径的一部分,更符合REST资源的层次结构。要启用矩阵变量支持,需要配置UrlPathHelper的removeSemicolonContent属性为false。通过@MatrixVariable注解,可以将矩阵变量绑定到控制器方法参数上,灵活处理复杂的URL路径参数。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.context.annotation.Configuration;
/**
* 配置类,启用矩阵变量支持
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 不移除分号内容,从而支持矩阵变量
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
/**
* 演示矩阵变量的使用
*/
@Controller
@RequestMapping("/catalog")
public class MatrixVariableController {
/**
* 基本矩阵变量用法
* URL示例: /catalog/products;category=books;price=50
*/
@GetMapping("/products")
@ResponseBody
public String findProducts(
@MatrixVariable(name = "category", required = false) String category,
@MatrixVariable(name = "price", required = false) Integer price) {
StringBuilder result = new StringBuilder("Products with ");
if (category != null) {
result.append("category: ").append(category).append(" ");
}
if (price != null) {
result.append("price: ").append(price);
}
return result.toString();
}
/**
* 在多路径段中使用矩阵变量
* URL示例: /catalog/brands;country=germany/products;category=electronics
*/
@GetMapping("/brands/{brandId}/products")
@ResponseBody
public String findProductsByBrand(
@PathVariable String brandId,
@MatrixVariable(name = "country", pathVar = "brands") String country,
@MatrixVariable(name = "category", pathVar = "products") String category) {
return "Brand: " + brandId +
", Country: " + country +
", Category: " + category;
}
/**
* 处理多值矩阵变量
* URL示例: /catalog/filter;colors=red,green,blue;sizes=S,M,L
*/
@GetMapping("/filter")
@ResponseBody
public String filterProducts(
@MatrixVariable(name = "colors") List<String> colors,
@MatrixVariable(name = "sizes") List<String> sizes) {
return "Filtering products by colors: " + colors +
" and sizes: " + sizes;
}
/**
* 处理全部矩阵变量
* URL示例: /catalog/search;minPrice=100;maxPrice=500;brand=apple;inStock=true
*/
@GetMapping("/search")
@ResponseBody
public String searchProducts(
@MatrixVariable Map<String, Object> matrixVars) {
return "Search criteria: " + matrixVars;
}
/**
* 结合路径变量和矩阵变量
* URL示例: /catalog/categories/electronics;featured=true/products
*/
@GetMapping("/categories/{category}/products")
@ResponseBody
public String getProductsByCategory(
@PathVariable String category,
@MatrixVariable(name = "featured", required = false) Boolean featured) {
return "Category: " + category +
(featured != null ? ", Featured: " + featured : "");
}
/**
* 处理URI模板变量
* URL示例: /catalog/items/123-456-789
*/
@GetMapping("/items/{itemCode}")
@ResponseBody
public String getItemByCode(@PathVariable String itemCode) {
// 手动解析复合ID
String[] parts = itemCode.split("-");
return "Item type: " + parts[0] +
", Vendor: " + parts[1] +
", Product: " + parts[2];
}
/**
* 结合正则表达式、路径变量和矩阵变量
* URL示例: /catalog/2023/spring;region=europe/collection
*/
@GetMapping("/{year:\\d{4}}/{season};region={region}/collection")
@ResponseBody
public String getSeasonalCollection(
@PathVariable Integer year,
@PathVariable String season,
@MatrixVariable String region) {
return year + " " + season + " collection for " + region;
}
}
五、自定义请求映射与注解组合
SpringMVC的注解系统支持元注解(meta-annotations)机制,允许开发者创建自定义注解,组合和扩展现有注解功能。通过创建自定义注解,可以封装常用的请求映射模式,使代码更加简洁和标准化。自定义注解通常包含@RequestMapping或其变体,以及其他相关注解如@ResponseBody、@ResponseStatus等。这种方式特别适合定义API版本控制、统一响应格式、权限检查等横切关注点。通过自定义注解,可以在团队中建立标准化的API开发规范,提高代码一致性和可维护性。
import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import java.lang.annotation.*;
/**
* 自定义API版本化注解
* 组合了@RequestMapping,并添加了版本前缀
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping("/v1")
public @interface ApiV1 {
/**
* 指定路径
*/
String[] value() default {};
}
/**
* 自定义GET请求注解
* 组合了@GetMapping和@ResponseBody
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@GetMapping
@ResponseBody
public @interface ApiGet {
/**
* 指定路径
*/
String[] value() default {};
/**
* 指定生产的媒体类型
*/
String[] produces() default {MediaType.APPLICATION_JSON_VALUE};
}
/**
* 自定义POST请求注解
* 组合了@PostMapping、@ResponseBody和@ResponseStatus
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@PostMapping
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
public @interface ApiPost {
/**
* 指定路径
*/
String[] value() default {};
/**
* 指定消费的媒体类型
*/
String[] consumes() default {MediaType.APPLICATION_JSON_VALUE};
/**
* 指定生产的媒体类型
*/
String[] produces() default {MediaType.APPLICATION_JSON_VALUE};
}
/**
* 使用自定义注解的控制器
*/
@RestController
@ApiV1
@RequestMapping("/custom")
public class CustomAnnotationController {
private final ItemService itemService;
public CustomAnnotationController(ItemService itemService) {
this.itemService = itemService;
}
/**
* 使用自定义@ApiGet注解
* 等同于@GetMapping + @ResponseBody
* URL: /v1/custom/items
*/
@ApiGet("/items")
public List<Item> getAllItems() {
return itemService.findAll();
}
/**
* 使用自定义@ApiPost注解
* 等同于@PostMapping + @ResponseBody + @ResponseStatus(CREATED)
* URL: /v1/custom/items
*/
@ApiPost("/items")
public Item createItem(@Valid @RequestBody Item item) {
return itemService.create(item);
}
/**
* 自定义注解与标准注解组合使用
* URL: /v1/custom/items/{id}
*/
@ApiGet("/items/{id}")
public ResponseEntity<Item> getItemById(@PathVariable Long id) {
return itemService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}
/**
* 自定义安全检查注解
* 组合了权限检查逻辑
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@PreAuthorize("hasRole('ADMIN')") // Spring Security注解
public @interface AdminOnly {
}
/**
* 更复杂的自定义注解示例
* 组合了多个横切关注点
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@PutMapping
@ResponseBody
@AdminOnly
@Transactional
public @interface AdminUpdate {
/**
* 指定路径
*/
String[] value() default {};
}
/**
* 使用安全相关自定义注解
*/
@RestController
@RequestMapping("/admin")
public class AdminController {
/**
* 使用组合了多个关注点的自定义注解
*/
@AdminUpdate("/settings/{id}")
public Setting updateSetting(
@PathVariable Long id,
@Valid @RequestBody Setting setting) {
return settingService.update(id, setting);
}
}
六、请求映射最佳实践与性能优化
在实际项目中,合理设计和配置请求映射对于提高应用性能和可维护性至关重要。首先,应避免过于复杂的URL模式和模糊的映射条件,精确指定HTTP方法和路径,减少不必要的条件判断。其次,对于大型应用,应基于功能模块或资源类型组织控制器,并利用类级别的@RequestMapping设置基础路径,保持URL结构清晰。对于频繁访问的API,可以配置HandlerMapping的缓存设置,提高请求分发效率。此外,还应注意避免映射冲突,使用AntPathMatcher配置更精细的路径匹配策略,并定期检查不必要的请求映射,减少内存占用和请求处理开销。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* RequestMapping配置最佳实践示例
*/
@Configuration
public class RequestMappingConfig implements WebMvcConfigurer {
/**
* 配置高性能的路径匹配器(Spring 5.3+)
* 使用PathPatternParser替代传统的AntPathMatcher
*/
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setPatternParser(new PathPatternParser());
// 设置请求映射的缓存大小
handlerMapping.setCacheSize(1024);
// 设置映射注册顺序敏感
handlerMapping.setUseSuffixPatternMatch(false);
handlerMapping.setUseTrailingSlashMatch(false);
return handlerMapping;
}
}
/**
* 控制器层次结构示例 - 基础控制器接口
* 定义共享的请求映射和行为
*/
@RequestMapping("/api/v1")
public interface BaseApi<T, ID> {
@GetMapping
List<T> findAll();
@GetMapping("/{id}")
ResponseEntity<T> findById(@PathVariable ID id);
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
T create(@Valid @RequestBody T entity);
@PutMapping("/{id}")
ResponseEntity<T> update(@PathVariable ID id, @Valid @RequestBody T entity);
@DeleteMapping("/{id}")
ResponseEntity<Void> delete(@PathVariable ID id);
}
/**
* 用户API控制器 - 继承基础API接口
* 遵循REST最佳实践
*/
@RestController
@RequestMapping("/users")
public class UserApiController implements BaseApi<User, Long> {
private final UserService userService;
public UserApiController(UserService userService) {
this.userService = userService;
}
// 实现基础接口方法
@Override
public List<User> findAll() {
return userService.findAll();
}
@Override
public ResponseEntity<User> findById(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@Override
public User create(@Valid @RequestBody User user) {
return userService.create(user);
}
@Override
public ResponseEntity<User> update(@PathVariable Long id, @Valid @RequestBody User user) {
return userService.update(id, user)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@Override
public ResponseEntity<Void> delete(@PathVariable Long id) {
boolean deleted = userService.delete(id);
return deleted ?
ResponseEntity.noContent().build() :
ResponseEntity.notFound().build();
}
/**
* 扩展基础API - 用户特定端点
* 展示如何在保持API一致性的同时添加特定功能
*/
@GetMapping("/search")
public List<User> searchUsers(
@RequestParam(required = false) String name,
@RequestParam(required = false) String email) {
return userService.search(name, email);
}
/**
* 嵌套资源示例 - 用户角色
*/
@GetMapping("/{userId}/roles")
public List<Role> getUserRoles(@PathVariable Long userId) {
return userService.findRolesByUserId(userId);
}
}
/**
* 高性能控制器示例
* 应用请求映射最佳实践
*/
@RestController
@RequestMapping("/api/v1/reports")
public class ReportController {
private final ReportService reportService;
public ReportController(ReportService reportService) {
this.reportService = reportService;
}
/**
* 使用良好定义的路径变量和请求参数
* 避免模糊匹配,提高分发效率
*/
@GetMapping("/{year}/{month}")
public Report getMonthlyReport(
@PathVariable int year,
@PathVariable int month,
@RequestParam(defaultValue = "summary") String type) {
validateYearMonth(year, month);
return reportService.generateReport(year, month, type);
}
/**
* 限定请求参数类型,减少运行时类型转换
*/
@GetMapping("/sales")
public SalesReport getSalesReport(
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate from,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate to,
@RequestParam(defaultValue = "DAILY") ReportGranularity granularity) {
validateDateRange(from, to);
return reportService.generateSalesReport(from, to, granularity);
}
/**
* 高效处理大文件下载
* 使用produces属性精确控制内容类型
*/
@GetMapping(value = "/export", produces = "application/vnd.ms-excel")
public ResponseEntity<Resource> exportReport(
@RequestParam int year,
@RequestParam(required = false) Integer month) {
Resource file = reportService.generateExcelReport(year, month);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"report.xlsx\"")
.body(file);
}
/**
* 参数验证方法
*/
private void validateYearMonth(int year, int month) {
if (year < 2000 || year > LocalDate.now().getYear()) {
throw new IllegalArgumentException("Invalid year: " + year);
}
if (month < 1 || month > 12) {
throw new IllegalArgumentException("Invalid month: " + month);
}
}
private void validateDateRange(LocalDate from, LocalDate to) {
if (from.isAfter(to)) {
throw new IllegalArgumentException("Start date must be before end date");
}
if (from.isBefore(LocalDate.now().minusYears(5))) {
throw new IllegalArgumentException("Reports only available for the last 5 years");
}
}
}
总结
@RequestMapping是SpringMVC框架中最为核心和强大的注解之一,通过本文对其高级用法的探讨,我们深入了解了路径模式与变量提取、请求条件组合、注解变体的语义化应用、URI模板与矩阵变量支持以及自定义注解组合等高级特性。这些功能为构建灵活、高效的Web应用提供了坚实基础。在实际应用中,开发者应遵循最佳实践,合理设计URL结构,精确指定请求条件,避免过于复杂的映射规则,并根据应用特点优化性能配置。通过合理运用@RequestMapping的高级特性,可以构建出更加语义化、易于维护的API,提高开发效率和代码质量。对于大型应用,建议结合Spring的AOP机制和自定义注解,进一步抽象和简化请求映射逻辑,实现更加优雅的解决方案。随着微服务架构的普及,掌握@RequestMapping的高级用法对于构建标准化、高性能的RESTful API变得尤为重要。