SpringMVC哀求映射:@RequestMapping的高级用法

打印 上一主题 下一主题

主题 989|帖子 989|积分 2967



  
引言

在SpringMVC框架中,哀求映射是连接客户端哀求与服务器处置惩罚逻辑的桥梁。@RequestMapping注解作为SpringMVC最核心的注解之一,为开发者提供了强大而灵活的URL映射机制。随着Web应用复杂度的提高,简单的URL匹配已不能满足今世应用开发的需求。深入明白@RequestMapping的高级特性和用法,对于构建健壮、灵活的Web应用至关紧张。本文将深入探讨@RequestMapping的高级用法,包括复杂路径匹配、多维度哀求条件组合、注解变体的应用场景以及RESTful API的最佳实践,资助开发者更加高效地使用这一强大工具,构建出更加优雅的Web应用。
一、路径模式与变量提取

@RequestMapping支持多种路径匹配模式,从简单的精确匹配到复杂的通配符和路径变量。路径变量使用{varName}语法定义,可以通过@PathVariable注解将其绑定到方法参数。SpringMVC还支持正则表达式路径变量{varName:regex},用于更精确的匹配控制。对于层次化的资源路径,可以使用**通配符捕获多级路径。这些高级匹配特性使得URL设计更加灵活,能够优雅地表达资源之间的层次关系,同时通过路径变量直观地通报参数,符合RESTful设计理念。
  1. import org.springframework.stereotype.Controller;
  2. import org.springframework.web.bind.annotation.PathVariable;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. /**
  6. * 演示@RequestMapping的高级路径匹配
  7. */
  8. @Controller
  9. @RequestMapping("/api")
  10. public class AdvancedPathMappingController {
  11.    
  12.     /**
  13.      * 基本路径变量
  14.      * URL示例: /api/users/123
  15.      */
  16.     @RequestMapping("/users/{userId}")
  17.     @ResponseBody
  18.     public String getUserById(@PathVariable("userId") Long userId) {
  19.         return "User ID: " + userId;
  20.     }
  21.    
  22.     /**
  23.      * 多个路径变量
  24.      * URL示例: /api/users/123/orders/456
  25.      */
  26.     @RequestMapping("/users/{userId}/orders/{orderId}")
  27.     @ResponseBody
  28.     public String getUserOrder(
  29.             @PathVariable("userId") Long userId,
  30.             @PathVariable("orderId") Long orderId) {
  31.         return "User ID: " + userId + ", Order ID: " + orderId;
  32.     }
  33.    
  34.     /**
  35.      * 使用正则表达式限制路径变量
  36.      * 仅匹配5位数字的产品代码
  37.      * URL示例: /api/products/12345
  38.      */
  39.     @RequestMapping("/products/{productCode:[0-9]{5}}")
  40.     @ResponseBody
  41.     public String getProductByCode(@PathVariable("productCode") String productCode) {
  42.         return "Product code: " + productCode;
  43.     }
  44.    
  45.     /**
  46.      * 通配符路径匹配
  47.      * 匹配如:/api/files/documents/report.pdf
  48.      */
  49.     @RequestMapping("/files/**")
  50.     @ResponseBody
  51.     public String getFile() {
  52.         return "File path: " + request.getRequestURI();
  53.     }
  54.    
  55.     /**
  56.      * 路径变量与通配符结合
  57.      * 匹配如:/api/repos/myorg/myrepo/tree/master/src/main
  58.      */
  59.     @RequestMapping("/repos/{org}/{repo}/tree/{branch}/**")
  60.     @ResponseBody
  61.     public String getRepositoryContent(
  62.             @PathVariable("org") String organization,
  63.             @PathVariable("repo") String repository,
  64.             @PathVariable("branch") String branch) {
  65.         String fullPath = request.getRequestURI();
  66.         String relativePath = fullPath.substring(fullPath.indexOf("/tree/" + branch) +
  67.                 ("/tree/" + branch).length());
  68.         
  69.         return "Organization: " + organization +
  70.                ", Repository: " + repository +
  71.                ", Branch: " + branch +
  72.                ", Path: " + relativePath;
  73.     }
  74.    
  75.     /**
  76.      * 可选路径变量(通过正则表达式实现)
  77.      * 匹配:/api/search 和 /api/search/query
  78.      */
  79.     @RequestMapping("/search{/query:.*}")
  80.     @ResponseBody
  81.     public String search(@PathVariable(value = "query", required = false) String query) {
  82.         if (query == null || query.isEmpty()) {
  83.             return "Search home page";
  84.         }
  85.         return "Search results for: " + query.substring(1); // 去除开头的斜杠
  86.     }
  87. }
复制代码
二、哀求条件组合与精确控制

@RequestMapping不仅可以基于URL路径举行匹配,还支持多种哀求条件的组合,包括HTTP方法(method)、哀求参数(params)、哀求头(headers)、媒体类型(consumes/produces)等。通过组合这些条件,可以实现对哀求的精确筛选和控制。对于复杂的Web应用,差别的客户端(如浏览器、移动应用、API客户端)可能需要差别的处置惩罚逻辑,通过哀求条件组合可以优雅地解决这一题目。此外,基于Content-Type的映射对于构建支持多种数据格式的API尤为紧张,可以根据客户端期望的数据格式提供相应的处置惩罚逻辑。
  1. import org.springframework.stereotype.Controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RequestMethod;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. /**
  6. * 演示@RequestMapping的请求条件组合
  7. */
  8. @Controller
  9. @RequestMapping("/products")
  10. public class RequestConditionController {
  11.    
  12.     /**
  13.      * 基于HTTP方法的映射(不使用便捷注解)
  14.      * 仅处理GET请求
  15.      */
  16.     @RequestMapping(value = "", method = RequestMethod.GET)
  17.     @ResponseBody
  18.     public String listProducts() {
  19.         return "Product list";
  20.     }
  21.    
  22.     /**
  23.      * 基于请求参数的映射
  24.      * 仅当请求包含'category'参数且值为'books'时匹配
  25.      * URL示例: /products/filter?category=books
  26.      */
  27.     @RequestMapping(value = "/filter", params = "category=books")
  28.     @ResponseBody
  29.     public String filterBookProducts() {
  30.         return "Filtered book products";
  31.     }
  32.    
  33.     /**
  34.      * 带有多个请求参数条件
  35.      * 要求有sort参数,但没有order参数
  36.      * URL示例: /products/filter?category=electronics&sort=price
  37.      */
  38.     @RequestMapping(
  39.         value = "/filter",
  40.         params = {"category=electronics", "sort", "!order"}
  41.     )
  42.     @ResponseBody
  43.     public String filterElectronicsProductsSorted() {
  44.         return "Filtered electronics products, sorted";
  45.     }
  46.    
  47.     /**
  48.      * 基于请求头的映射
  49.      * 仅匹配来自移动设备的请求(通过User-Agent判断)
  50.      */
  51.     @RequestMapping(
  52.         value = "/view",
  53.         headers = "User-Agent=Mozilla/5.0.*Android.*"
  54.     )
  55.     @ResponseBody
  56.     public String viewProductOnMobile() {
  57.         return "Mobile view of product";
  58.     }
  59.    
  60.     /**
  61.      * 处理来自特定客户端的请求
  62.      * 基于自定义请求头进行匹配
  63.      */
  64.     @RequestMapping(
  65.         value = "/view",
  66.         headers = "X-API-VERSION=1"
  67.     )
  68.     @ResponseBody
  69.     public String viewProductV1API() {
  70.         return "Product view API v1";
  71.     }
  72.    
  73.     /**
  74.      * 基于Accept头的映射(produces属性)
  75.      * 客户端期望接收HTML响应
  76.      */
  77.     @RequestMapping(
  78.         value = "/details/{id}",
  79.         produces = "text/html"
  80.     )
  81.     public String getProductDetailsHtml() {
  82.         return "product/details"; // 返回视图名称
  83.     }
  84.    
  85.     /**
  86.      * 基于Accept头的映射(produces属性)
  87.      * 客户端期望接收JSON响应
  88.      */
  89.     @RequestMapping(
  90.         value = "/details/{id}",
  91.         produces = "application/json"
  92.     )
  93.     @ResponseBody
  94.     public Product getProductDetailsJson(@PathVariable Long id) {
  95.         Product product = productService.findById(id);
  96.         return product; // 返回JSON数据
  97.     }
  98.    
  99.     /**
  100.      * 基于Content-Type头的映射(consumes属性)
  101.      * 处理JSON格式的请求体
  102.      */
  103.     @RequestMapping(
  104.         value = "",
  105.         method = RequestMethod.POST,
  106.         consumes = "application/json"
  107.     )
  108.     @ResponseBody
  109.     public String createProductFromJson(@RequestBody Product product) {
  110.         productService.save(product);
  111.         return "Product created from JSON";
  112.     }
  113.    
  114.     /**
  115.      * 基于Content-Type头的映射(consumes属性)
  116.      * 处理XML格式的请求体
  117.      */
  118.     @RequestMapping(
  119.         value = "",
  120.         method = RequestMethod.POST,
  121.         consumes = "application/xml"
  122.     )
  123.     @ResponseBody
  124.     public String createProductFromXml(@RequestBody Product product) {
  125.         productService.save(product);
  126.         return "Product created from XML";
  127.     }
  128.    
  129.     /**
  130.      * 复杂条件组合示例
  131.      * 同时指定HTTP方法、请求参数、请求头、Content-Type和Accept
  132.      */
  133.     @RequestMapping(
  134.         value = "/process",
  135.         method = RequestMethod.PUT,
  136.         params = "action=update",
  137.         headers = "X-API-TOKEN",
  138.         consumes = "application/json",
  139.         produces = "application/json"
  140.     )
  141.     @ResponseBody
  142.     public Product processProductUpdate(@RequestBody Product product) {
  143.         return productService.update(product);
  144.     }
  145. }
复制代码
三、哀求映射注解变体与语义化

从Spring 4.3开始,框架引入了一系列@RequestMapping的变体注解,包括@GetMapping、@PostMapping、@PutMapping、@DeleteMapping和@PatchMapping,用于简化特定HTTP方法的映射定义。这些变体注解不仅简化了代码,还提高了代码的语义化,使API意图更加明确。在RESTful API开发中,选择合适的HTTP方法对于表达资源操作的语义至关紧张。@GetMapping用于资源获取,@PostMapping用于资源创建,@PutMapping用于资源完整更新,@PatchMapping用于资源部分更新,@DeleteMapping用于资源删除。通过使用这些语义化注解,可以构建出符合REST原则的API。
  1. import org.springframework.web.bind.annotation.*;
  2. import org.springframework.http.ResponseEntity;
  3. import org.springframework.http.HttpStatus;
  4. /**
  5. * 使用@RequestMapping变体注解构建RESTful API
  6. */
  7. @RestController
  8. @RequestMapping("/api/articles")
  9. public class ArticleApiController {
  10.    
  11.     private final ArticleService articleService;
  12.    
  13.     public ArticleApiController(ArticleService articleService) {
  14.         this.articleService = articleService;
  15.     }
  16.    
  17.     /**
  18.      * 获取文章列表
  19.      * 使用@GetMapping替代@RequestMapping(method = RequestMethod.GET)
  20.      */
  21.     @GetMapping
  22.     public List<Article> getAllArticles() {
  23.         return articleService.findAll();
  24.     }
  25.    
  26.     /**
  27.      * 获取特定文章
  28.      * @PathVariable直接注解在路径参数上
  29.      */
  30.     @GetMapping("/{id}")
  31.     public ResponseEntity<Article> getArticleById(@PathVariable Long id) {
  32.         return articleService.findById(id)
  33.                 .map(ResponseEntity::ok)
  34.                 .orElse(ResponseEntity.notFound().build());
  35.     }
  36.    
  37.     /**
  38.      * 创建新文章
  39.      * 使用@PostMapping替代@RequestMapping(method = RequestMethod.POST)
  40.      */
  41.     @PostMapping
  42.     @ResponseStatus(HttpStatus.CREATED)
  43.     public Article createArticle(@Valid @RequestBody Article article) {
  44.         return articleService.create(article);
  45.     }
  46.    
  47.     /**
  48.      * 完全更新文章
  49.      * 使用@PutMapping替代@RequestMapping(method = RequestMethod.PUT)
  50.      */
  51.     @PutMapping("/{id}")
  52.     public ResponseEntity<Article> updateArticle(
  53.             @PathVariable Long id,
  54.             @Valid @RequestBody Article article) {
  55.         
  56.         return articleService.update(id, article)
  57.                 .map(ResponseEntity::ok)
  58.                 .orElse(ResponseEntity.notFound().build());
  59.     }
  60.    
  61.     /**
  62.      * 部分更新文章
  63.      * 使用@PatchMapping替代@RequestMapping(method = RequestMethod.PATCH)
  64.      */
  65.     @PatchMapping("/{id}")
  66.     public ResponseEntity<Article> partialUpdateArticle(
  67.             @PathVariable Long id,
  68.             @RequestBody Map<String, Object> updates) {
  69.         
  70.         return articleService.partialUpdate(id, updates)
  71.                 .map(ResponseEntity::ok)
  72.                 .orElse(ResponseEntity.notFound().build());
  73.     }
  74.    
  75.     /**
  76.      * 删除文章
  77.      * 使用@DeleteMapping替代@RequestMapping(method = RequestMethod.DELETE)
  78.      */
  79.     @DeleteMapping("/{id}")
  80.     public ResponseEntity<Void> deleteArticle(@PathVariable Long id) {
  81.         boolean deleted = articleService.delete(id);
  82.         
  83.         if (deleted) {
  84.             return ResponseEntity.noContent().build();
  85.         } else {
  86.             return ResponseEntity.notFound().build();
  87.         }
  88.     }
  89.    
  90.     /**
  91.      * 嵌套资源:获取文章评论
  92.      * 展示如何处理资源之间的关系
  93.      */
  94.     @GetMapping("/{articleId}/comments")
  95.     public List<Comment> getArticleComments(@PathVariable Long articleId) {
  96.         return commentService.findByArticleId(articleId);
  97.     }
  98.    
  99.     /**
  100.      * 嵌套资源:添加文章评论
  101.      */
  102.     @PostMapping("/{articleId}/comments")
  103.     @ResponseStatus(HttpStatus.CREATED)
  104.     public Comment addArticleComment(
  105.             @PathVariable Long articleId,
  106.             @Valid @RequestBody Comment comment) {
  107.         
  108.         comment.setArticleId(articleId);
  109.         return commentService.create(comment);
  110.     }
  111.    
  112.     /**
  113.      * 使用请求参数过滤资源
  114.      * 展示如何与@RequestParam结合使用
  115.      */
  116.     @GetMapping("/search")
  117.     public List<Article> searchArticles(
  118.             @RequestParam(required = false) String title,
  119.             @RequestParam(required = false) String author,
  120.             @RequestParam(defaultValue = "0") int page,
  121.             @RequestParam(defaultValue = "10") int size) {
  122.         
  123.         return articleService.search(title, author, page, size);
  124.     }
  125. }
复制代码
四、URI模板与矩阵变量支持

SpringMVC支持更高级的URI模板特性,如矩阵变量(Matrix Variables)。矩阵变量是URI路径中的键值对,使用分号(;)分隔,形如/path;param1=value1;param2=value2。这一特性在RESTful API中很有用,尤其是需要在路径中通报多个可选参数时。与查询参数差别,矩阵变量是路径的一部分,更符合REST资源的层次结构。要启用矩阵变量支持,需要设置UrlPathHelper的removeSemicolonContent属性为false。通过@MatrixVariable注解,可以将矩阵变量绑定到控制器方法参数上,灵活处置惩罚复杂的URL路径参数。
  1. import org.springframework.stereotype.Controller;
  2. import org.springframework.web.bind.annotation.*;
  3. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  4. import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
  5. import org.springframework.web.util.UrlPathHelper;
  6. import org.springframework.context.annotation.Configuration;
  7. /**
  8. * 配置类,启用矩阵变量支持
  9. */
  10. @Configuration
  11. public class WebConfig implements WebMvcConfigurer {
  12.    
  13.     @Override
  14.     public void configurePathMatch(PathMatchConfigurer configurer) {
  15.         UrlPathHelper urlPathHelper = new UrlPathHelper();
  16.         // 不移除分号内容,从而支持矩阵变量
  17.         urlPathHelper.setRemoveSemicolonContent(false);
  18.         configurer.setUrlPathHelper(urlPathHelper);
  19.     }
  20. }
  21. /**
  22. * 演示矩阵变量的使用
  23. */
  24. @Controller
  25. @RequestMapping("/catalog")
  26. public class MatrixVariableController {
  27.    
  28.     /**
  29.      * 基本矩阵变量用法
  30.      * URL示例: /catalog/products;category=books;price=50
  31.      */
  32.     @GetMapping("/products")
  33.     @ResponseBody
  34.     public String findProducts(
  35.             @MatrixVariable(name = "category", required = false) String category,
  36.             @MatrixVariable(name = "price", required = false) Integer price) {
  37.         
  38.         StringBuilder result = new StringBuilder("Products with ");
  39.         if (category != null) {
  40.             result.append("category: ").append(category).append(" ");
  41.         }
  42.         if (price != null) {
  43.             result.append("price: ").append(price);
  44.         }
  45.         
  46.         return result.toString();
  47.     }
  48.    
  49.     /**
  50.      * 在多路径段中使用矩阵变量
  51.      * URL示例: /catalog/brands;country=germany/products;category=electronics
  52.      */
  53.     @GetMapping("/brands/{brandId}/products")
  54.     @ResponseBody
  55.     public String findProductsByBrand(
  56.             @PathVariable String brandId,
  57.             @MatrixVariable(name = "country", pathVar = "brands") String country,
  58.             @MatrixVariable(name = "category", pathVar = "products") String category) {
  59.         
  60.         return "Brand: " + brandId +
  61.                ", Country: " + country +
  62.                ", Category: " + category;
  63.     }
  64.    
  65.     /**
  66.      * 处理多值矩阵变量
  67.      * URL示例: /catalog/filter;colors=red,green,blue;sizes=S,M,L
  68.      */
  69.     @GetMapping("/filter")
  70.     @ResponseBody
  71.     public String filterProducts(
  72.             @MatrixVariable(name = "colors") List<String> colors,
  73.             @MatrixVariable(name = "sizes") List<String> sizes) {
  74.         
  75.         return "Filtering products by colors: " + colors +
  76.                " and sizes: " + sizes;
  77.     }
  78.    
  79.     /**
  80.      * 处理全部矩阵变量
  81.      * URL示例: /catalog/search;minPrice=100;maxPrice=500;brand=apple;inStock=true
  82.      */
  83.     @GetMapping("/search")
  84.     @ResponseBody
  85.     public String searchProducts(
  86.             @MatrixVariable Map<String, Object> matrixVars) {
  87.         
  88.         return "Search criteria: " + matrixVars;
  89.     }
  90.    
  91.     /**
  92.      * 结合路径变量和矩阵变量
  93.      * URL示例: /catalog/categories/electronics;featured=true/products
  94.      */
  95.     @GetMapping("/categories/{category}/products")
  96.     @ResponseBody
  97.     public String getProductsByCategory(
  98.             @PathVariable String category,
  99.             @MatrixVariable(name = "featured", required = false) Boolean featured) {
  100.         
  101.         return "Category: " + category +
  102.                (featured != null ? ", Featured: " + featured : "");
  103.     }
  104.    
  105.     /**
  106.      * 处理URI模板变量
  107.      * URL示例: /catalog/items/123-456-789
  108.      */
  109.     @GetMapping("/items/{itemCode}")
  110.     @ResponseBody
  111.     public String getItemByCode(@PathVariable String itemCode) {
  112.         // 手动解析复合ID
  113.         String[] parts = itemCode.split("-");
  114.         return "Item type: " + parts[0] +
  115.                ", Vendor: " + parts[1] +
  116.                ", Product: " + parts[2];
  117.     }
  118.    
  119.     /**
  120.      * 结合正则表达式、路径变量和矩阵变量
  121.      * URL示例: /catalog/2023/spring;region=europe/collection
  122.      */
  123.     @GetMapping("/{year:\\d{4}}/{season};region={region}/collection")
  124.     @ResponseBody
  125.     public String getSeasonalCollection(
  126.             @PathVariable Integer year,
  127.             @PathVariable String season,
  128.             @MatrixVariable String region) {
  129.         
  130.         return year + " " + season + " collection for " + region;
  131.     }
  132. }
复制代码
五、自定义哀求映射与注解组合

SpringMVC的注解系统支持元注解(meta-annotations)机制,允许开发者创建自定义注解,组合和扩展现有注解功能。通过创建自定义注解,可以封装常用的哀求映射模式,使代码更加简洁和标准化。自定义注解通常包含@RequestMapping或其变体,以及其他相关注解如@ResponseBody、@ResponseStatus等。这种方式特别得当定义API版本控制、同一响应格式、权限查抄等横切关注点。通过自定义注解,可以在团队中创建标准化的API开发规范,提高代码一致性和可维护性。
  1. import org.springframework.web.bind.annotation.*;
  2. import org.springframework.http.HttpStatus;
  3. import org.springframework.http.MediaType;
  4. import java.lang.annotation.*;
  5. /**
  6. * 自定义API版本化注解
  7. * 组合了@RequestMapping,并添加了版本前缀
  8. */
  9. @Target({ElementType.TYPE, ElementType.METHOD})
  10. @Retention(RetentionPolicy.RUNTIME)
  11. @Documented
  12. @RequestMapping("/v1")
  13. public @interface ApiV1 {
  14.     /**
  15.      * 指定路径
  16.      */
  17.     String[] value() default {};
  18. }
  19. /**
  20. * 自定义GET请求注解
  21. * 组合了@GetMapping和@ResponseBody
  22. */
  23. @Target(ElementType.METHOD)
  24. @Retention(RetentionPolicy.RUNTIME)
  25. @Documented
  26. @GetMapping
  27. @ResponseBody
  28. public @interface ApiGet {
  29.     /**
  30.      * 指定路径
  31.      */
  32.     String[] value() default {};
  33.    
  34.     /**
  35.      * 指定生产的媒体类型
  36.      */
  37.     String[] produces() default {MediaType.APPLICATION_JSON_VALUE};
  38. }
  39. /**
  40. * 自定义POST请求注解
  41. * 组合了@PostMapping、@ResponseBody和@ResponseStatus
  42. */
  43. @Target(ElementType.METHOD)
  44. @Retention(RetentionPolicy.RUNTIME)
  45. @Documented
  46. @PostMapping
  47. @ResponseBody
  48. @ResponseStatus(HttpStatus.CREATED)
  49. public @interface ApiPost {
  50.     /**
  51.      * 指定路径
  52.      */
  53.     String[] value() default {};
  54.    
  55.     /**
  56.      * 指定消费的媒体类型
  57.      */
  58.     String[] consumes() default {MediaType.APPLICATION_JSON_VALUE};
  59.    
  60.     /**
  61.      * 指定生产的媒体类型
  62.      */
  63.     String[] produces() default {MediaType.APPLICATION_JSON_VALUE};
  64. }
  65. /**
  66. * 使用自定义注解的控制器
  67. */
  68. @RestController
  69. @ApiV1
  70. @RequestMapping("/custom")
  71. public class CustomAnnotationController {
  72.    
  73.     private final ItemService itemService;
  74.    
  75.     public CustomAnnotationController(ItemService itemService) {
  76.         this.itemService = itemService;
  77.     }
  78.    
  79.     /**
  80.      * 使用自定义@ApiGet注解
  81.      * 等同于@GetMapping + @ResponseBody
  82.      * URL: /v1/custom/items
  83.      */
  84.     @ApiGet("/items")
  85.     public List<Item> getAllItems() {
  86.         return itemService.findAll();
  87.     }
  88.    
  89.     /**
  90.      * 使用自定义@ApiPost注解
  91.      * 等同于@PostMapping + @ResponseBody + @ResponseStatus(CREATED)
  92.      * URL: /v1/custom/items
  93.      */
  94.     @ApiPost("/items")
  95.     public Item createItem(@Valid @RequestBody Item item) {
  96.         return itemService.create(item);
  97.     }
  98.    
  99.     /**
  100.      * 自定义注解与标准注解组合使用
  101.      * URL: /v1/custom/items/{id}
  102.      */
  103.     @ApiGet("/items/{id}")
  104.     public ResponseEntity<Item> getItemById(@PathVariable Long id) {
  105.         return itemService.findById(id)
  106.                 .map(ResponseEntity::ok)
  107.                 .orElse(ResponseEntity.notFound().build());
  108.     }
  109. }
  110. /**
  111. * 自定义安全检查注解
  112. * 组合了权限检查逻辑
  113. */
  114. @Target(ElementType.METHOD)
  115. @Retention(RetentionPolicy.RUNTIME)
  116. @Documented
  117. @PreAuthorize("hasRole('ADMIN')")  // Spring Security注解
  118. public @interface AdminOnly {
  119. }
  120. /**
  121. * 更复杂的自定义注解示例
  122. * 组合了多个横切关注点
  123. */
  124. @Target(ElementType.METHOD)
  125. @Retention(RetentionPolicy.RUNTIME)
  126. @Documented
  127. @PutMapping
  128. @ResponseBody
  129. @AdminOnly
  130. @Transactional
  131. public @interface AdminUpdate {
  132.     /**
  133.      * 指定路径
  134.      */
  135.     String[] value() default {};
  136. }
  137. /**
  138. * 使用安全相关自定义注解
  139. */
  140. @RestController
  141. @RequestMapping("/admin")
  142. public class AdminController {
  143.    
  144.     /**
  145.      * 使用组合了多个关注点的自定义注解
  146.      */
  147.     @AdminUpdate("/settings/{id}")
  148.     public Setting updateSetting(
  149.             @PathVariable Long id,
  150.             @Valid @RequestBody Setting setting) {
  151.         
  152.         return settingService.update(id, setting);
  153.     }
  154. }
复制代码
六、哀求映射最佳实践与性能优化

在现实项目中,公道设计和设置哀求映射对于提高应用性能和可维护性至关紧张。起首,应避免过于复杂的URL模式和模糊的映射条件,精确指定HTTP方法和路径,减少不须要的条件判断。其次,对于大型应用,应基于功能模块或资源类型构造控制器,并使用类级别的@RequestMapping设置底子路径,保持URL结构清晰。对于频仍访问的API,可以设置HandlerMapping的缓存设置,提高哀求分发效率。此外,还应注意避免映射辩论,使用AntPathMatcher设置更精致的路径匹配策略,并定期查抄不须要的哀求映射,减少内存占用和哀求处置惩罚开销。
  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  4. import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  5. import org.springframework.web.util.pattern.PathPatternParser;
  6. /**
  7. * RequestMapping配置最佳实践示例
  8. */
  9. @Configuration
  10. public class RequestMappingConfig implements WebMvcConfigurer {
  11.    
  12.     /**
  13.      * 配置高性能的路径匹配器(Spring 5.3+)
  14.      * 使用PathPatternParser替代传统的AntPathMatcher
  15.      */
  16.     @Bean
  17.     public RequestMappingHandlerMapping requestMappingHandlerMapping() {
  18.         RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
  19.         handlerMapping.setPatternParser(new PathPatternParser());
  20.         // 设置请求映射的缓存大小
  21.         handlerMapping.setCacheSize(1024);
  22.         // 设置映射注册顺序敏感
  23.         handlerMapping.setUseSuffixPatternMatch(false);
  24.         handlerMapping.setUseTrailingSlashMatch(false);
  25.         return handlerMapping;
  26.     }
  27. }
  28. /**
  29. * 控制器层次结构示例 - 基础控制器接口
  30. * 定义共享的请求映射和行为
  31. */
  32. @RequestMapping("/api/v1")
  33. public interface BaseApi<T, ID> {
  34.    
  35.     @GetMapping
  36.     List<T> findAll();
  37.    
  38.     @GetMapping("/{id}")
  39.     ResponseEntity<T> findById(@PathVariable ID id);
  40.    
  41.     @PostMapping
  42.     @ResponseStatus(HttpStatus.CREATED)
  43.     T create(@Valid @RequestBody T entity);
  44.    
  45.     @PutMapping("/{id}")
  46.     ResponseEntity<T> update(@PathVariable ID id, @Valid @RequestBody T entity);
  47.    
  48.     @DeleteMapping("/{id}")
  49.     ResponseEntity<Void> delete(@PathVariable ID id);
  50. }
  51. /**
  52. * 用户API控制器 - 继承基础API接口
  53. * 遵循REST最佳实践
  54. */
  55. @RestController
  56. @RequestMapping("/users")
  57. public class UserApiController implements BaseApi<User, Long> {
  58.    
  59.     private final UserService userService;
  60.    
  61.     public UserApiController(UserService userService) {
  62.         this.userService = userService;
  63.     }
  64.    
  65.     // 实现基础接口方法
  66.     @Override
  67.     public List<User> findAll() {
  68.         return userService.findAll();
  69.     }
  70.    
  71.     @Override
  72.     public ResponseEntity<User> findById(@PathVariable Long id) {
  73.         return userService.findById(id)
  74.                 .map(ResponseEntity::ok)
  75.                 .orElse(ResponseEntity.notFound().build());
  76.     }
  77.    
  78.     @Override
  79.     public User create(@Valid @RequestBody User user) {
  80.         return userService.create(user);
  81.     }
  82.    
  83.     @Override
  84.     public ResponseEntity<User> update(@PathVariable Long id, @Valid @RequestBody User user) {
  85.         return userService.update(id, user)
  86.                 .map(ResponseEntity::ok)
  87.                 .orElse(ResponseEntity.notFound().build());
  88.     }
  89.    
  90.     @Override
  91.     public ResponseEntity<Void> delete(@PathVariable Long id) {
  92.         boolean deleted = userService.delete(id);
  93.         return deleted ?
  94.                 ResponseEntity.noContent().build() :
  95.                 ResponseEntity.notFound().build();
  96.     }
  97.    
  98.     /**
  99.      * 扩展基础API - 用户特定端点
  100.      * 展示如何在保持API一致性的同时添加特定功能
  101.      */
  102.     @GetMapping("/search")
  103.     public List<User> searchUsers(
  104.             @RequestParam(required = false) String name,
  105.             @RequestParam(required = false) String email) {
  106.         return userService.search(name, email);
  107.     }
  108.    
  109.     /**
  110.      * 嵌套资源示例 - 用户角色
  111.      */
  112.     @GetMapping("/{userId}/roles")
  113.     public List<Role> getUserRoles(@PathVariable Long userId) {
  114.         return userService.findRolesByUserId(userId);
  115.     }
  116. }
  117. /**
  118. * 高性能控制器示例
  119. * 应用请求映射最佳实践
  120. */
  121. @RestController
  122. @RequestMapping("/api/v1/reports")
  123. public class ReportController {
  124.    
  125.     private final ReportService reportService;
  126.    
  127.     public ReportController(ReportService reportService) {
  128.         this.reportService = reportService;
  129.     }
  130.    
  131.     /**
  132.      * 使用良好定义的路径变量和请求参数
  133.      * 避免模糊匹配,提高分发效率
  134.      */
  135.     @GetMapping("/{year}/{month}")
  136.     public Report getMonthlyReport(
  137.             @PathVariable int year,
  138.             @PathVariable int month,
  139.             @RequestParam(defaultValue = "summary") String type) {
  140.         
  141.         validateYearMonth(year, month);
  142.         return reportService.generateReport(year, month, type);
  143.     }
  144.    
  145.     /**
  146.      * 限定请求参数类型,减少运行时类型转换
  147.      */
  148.     @GetMapping("/sales")
  149.     public SalesReport getSalesReport(
  150.             @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate from,
  151.             @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate to,
  152.             @RequestParam(defaultValue = "DAILY") ReportGranularity granularity) {
  153.         
  154.         validateDateRange(from, to);
  155.         return reportService.generateSalesReport(from, to, granularity);
  156.     }
  157.    
  158.     /**
  159.      * 高效处理大文件下载
  160.      * 使用produces属性精确控制内容类型
  161.      */
  162.     @GetMapping(value = "/export", produces = "application/vnd.ms-excel")
  163.     public ResponseEntity<Resource> exportReport(
  164.             @RequestParam int year,
  165.             @RequestParam(required = false) Integer month) {
  166.         
  167.         Resource file = reportService.generateExcelReport(year, month);
  168.         
  169.         return ResponseEntity.ok()
  170.                 .header(HttpHeaders.CONTENT_DISPOSITION,
  171.                         "attachment; filename="report.xlsx"")
  172.                 .body(file);
  173.     }
  174.    
  175.     /**
  176.      * 参数验证方法
  177.      */
  178.     private void validateYearMonth(int year, int month) {
  179.         if (year < 2000 || year > LocalDate.now().getYear()) {
  180.             throw new IllegalArgumentException("Invalid year: " + year);
  181.         }
  182.         if (month < 1 || month > 12) {
  183.             throw new IllegalArgumentException("Invalid month: " + month);
  184.         }
  185.     }
  186.    
  187.     private void validateDateRange(LocalDate from, LocalDate to) {
  188.         if (from.isAfter(to)) {
  189.             throw new IllegalArgumentException("Start date must be before end date");
  190.         }
  191.         if (from.isBefore(LocalDate.now().minusYears(5))) {
  192.             throw new IllegalArgumentException("Reports only available for the last 5 years");
  193.         }
  194.     }
  195. }
复制代码
总结

@RequestMapping是SpringMVC框架中最为核心和强大的注解之一,通过本文对其高级用法的探讨,我们深入了解了路径模式与变量提取、哀求条件组合、注解变体的语义化应用、URI模板与矩阵变量支持以及自定义注解组合等高级特性。这些功能为构建灵活、高效的Web应用提供了坚实底子。在现实应用中,开发者应遵照最佳实践,公道设计URL结构,精确指定哀求条件,避免过于复杂的映射规则,并根据应用特点优化性能设置。通过公道运用@RequestMapping的高级特性,可以构建出更加语义化、易于维护的API,提高开发效率和代码质量。对于大型应用,建议联合Spring的AOP机制和自定义注解,进一步抽象和简化哀求映射逻辑,实现更加优雅的解决方案。随着微服务架构的遍及,把握@RequestMapping的高级用法对于构建标准化、高性能的RESTful API变得尤为紧张。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

半亩花草

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表