@PathVariable工作原理及URI解析参数

本文介绍了SpringMVC中参数解析绑定的工作原理,包括初始化时如何添加默认参数解析器、根据URL查找对应的HandlerMethod以及遍历MethodParameter数组来确定使用的HandlerMethodArgumentResolver。此外,还详细说明了@PathVariable注解的具体工作流程。

SpringMVC参数解析绑定的原理 https://zhuanlan.zhihu.com/p/33780880

  • SpringMVC初始化时,RequestMappingHandlerAdapter类会把一些默认的参数解析器添加到argumentResolvers中。当SpringMVC接收到请求后首先根据url查找对应的HandlerMethod。
  • 遍历HandlerMethod的MethodParameter数组
  • 根据MethodParameter的类型来查找确认使用哪个HandlerMethodArgumentResolver,遍历所有的argumentResolvers的supportsParameter(MethodParameter parameter)方法。。如果返回true,则表示查找成功,当前MethodParameter,使用该HandlerMethodArgumentResolver。这里确认大多都是根据参数的注解已经参数的Type来确认。
  • 解析参数,从request中解析出MethodParameter对应的参数,这里解析出来的结果都是String类型。
  • 转换参数,把对应String转换成具体方法所需要的类型,这里就包括了基本类型、对象、List、Set、Map

@PathVariable工作原理 参考戳这

  • 将/aa/{name}/bb根据/拆成 aa、{name}、bb
  • 将{name}替换成(.*),并纪录key为name
  • 同样将/aa/zhangsan/bb拆成aa、zhangsan、bb
  • 进行正则匹配,并根据group得到name=zhangsan
  • 将name=zhangsan放入request的一个attribute中
  • 根据反射和标注得到pathvariable名为name

需求

根据请求url找出数据库中与之匹配的url,截取参数

实现


   private Map<String, String> getPathParamFromUri(String uri, List<String> uriList){
       String[] input = uri.trim().split("/");
       String key = null;
       Optional<String> rtn = uriList.parallelStream()
               .filter(o -> matcherUrl(input, o)).findFirst();
       if (rtn.isPresent()) {
           key = rtn.get();
       }
       return matcherParamFromUrl(input, key);
   }
   
   private Map<String, String> matcherParamFromUrl(String[] input, String uri) {
       Map<String, String> rtn = new HashMap<>(5);
       String[] urls = uri.trim().split("/");
       for (int i = 0; i < urls.length; i++) {
           if (urls[i].contains("{") && urls[i].contains("}")) {
               rtn.put(urls[i].replace("{", "").replace("}", ""), input[i]);
           }
       }
       return rtn;
   }

   private boolean matcherUrl(String[] input, String uri) {
       Assert.notBlank(uri, "uri不能为空");
       String[] urls = uri.trim().split("/");
       if (input.length != urls.length) {
           return false;
       }
       for (int i = 0; i < urls.length; i++) {
           if (urls[i].contains("{") && urls[i].contains("}")) {
               continue;
           }
           if (!urls[i].equals(input[i])) {
               return false;
           }
       }
       return true;
   }
Spring Boot 中,`@RequestParam` 和 `@PathVariable` 都是用于从 HTTP 请求中提取数据的注解,但它们的**用途、数据来源和适用场景**有明显区别。下面我们从定义、使用方式、适用场景、底层原理等多个维度进行详细对比。 --- ## ✅ 一、基本区别总结 | 特性 | `@RequestParam` | `@PathVariable` | |------|------------------|------------------| | 作用 | 获取 URL 查询参数(Query Parameters) | 获取 URL 路径中的变量(URI Template Variables) | | 数据位置 | URL 的查询字符串部分(如 `?name=Tom`) | URL 路径中(如 `/user/123`) | | 示例 URL | `/user?name=Tom` | `/user/123` | | 适用请求类型 | GET、POST 等均可 | GET、POST 等均可 | | 是否支持默认值 | ✅ 支持(通过 `defaultValue`) | ❌ 不支持默认值 | | 是否可选 | ✅ 可选(通过 `required = false`) | ❌ 不可选,默认必须存在 | | 数据类型 | 支持基本类型、String、数组等 | 支持基本类型、String,不支持数组 | --- ## ✅ 二、详细解释 ### 1. `@RequestParam`:获取查询参数 用于从 URL 的查询字符串中提取参数值。 #### 示例代码: ```java @GetMapping("/user") public String getUser(@RequestParam String name, @RequestParam int age) { return "Name: " + name + ", Age: " + age; } ``` #### 请求示例: ``` GET /user?name=Tom&age=25 ``` #### 特点: - 适用于 `GET` 请求(GET 请求没有 body)。 - 用于提取 URL 中的查询参数(query parameters)。 - 支持设置默认值和可选参数。 #### 可选参数示例: ```java @RequestParam(name = "name", required = false, defaultValue = "Guest") String name ``` --- ### 2. `@PathVariable`:获取路径变量 用于从 URL 路径中提取变量值。 #### 示例代码: ```java @GetMapping("/user/{id}") public String getUserById(@PathVariable Long id) { return "User ID: " + id; } ``` #### 请求示例: ``` GET /user/123 ``` #### 特点: - 更符合 RESTful 风格的 URL 设计。 - 路径变量必须存在(默认 `required = true`),否则会抛出异常。 - 不支持默认值。 #### 多路径变量示例: ```java @GetMapping("/user/{userId}/order/{orderId}") public String getUserOrder( @PathVariable Long userId, @PathVariable Long orderId) { return "User ID: " + userId + ", Order ID: " + orderId; } ``` --- ## ✅ 三、底层原理对比 | 特性 | `@RequestParam` | `@PathVariable` | |------|------------------|------------------| | 数据来源 | URL 查询字符串(query string) | URI 路径(URI template) | | 解析器 | `RequestParamMethodArgumentResolver` | `PathVariableMethodArgumentResolver` | | 是否支持正则表达式 | ❌ | ✅(可通过路径表达式限制格式) | #### 示例:使用正则表达式限制路径变量格式 ```java @GetMapping("/user/{id:\\d+}") public String getUserById(@PathVariable Long id) { return "User ID: " + id; } ``` 该路径只接受数字类型的 `id`,如 `/user/123`,不接受 `/user/abc`。 --- ## ✅ 四、示例对比 ### 1. `@RequestParam` 示例 ```java @GetMapping("/search") public String search(@RequestParam String keyword, @RequestParam int page) { return "Searching for: " + keyword + ", Page: " + page; } ``` **请求:** ``` GET /search?keyword=spring&page=1 ``` ### 2. `@PathVariable` 示例 ```java @GetMapping("/user/{id}") public String getUser(@PathVariable Long id) { return "User ID: " + id; } ``` **请求:** ``` GET /user/123 ``` --- ## ✅ 五、如何选择? | 场景 | 推荐注解 | |------|----------| | 获取 URL 查询参数(如搜索关键词、分页参数) | `@RequestParam` | | 获取 RESTful 风格的路径变量(如用户 ID、订单 ID) | `@PathVariable` | | 参数是可选的 | `@RequestParam(required = false)` | | 需要限制参数格式(如必须为数字) | `@PathVariable` + 正则表达式 | --- ## ✅ 六、扩展:同时使用 `@RequestParam` 和 `@PathVariable` ```java @GetMapping("/user/{id}/orders") public String getUserOrders( @PathVariable Long id, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { return "User ID: " + id + ", Page: " + page + ", Size: " + size; } ``` **请求:** ``` GET /user/123/orders?page=2&size=20 ``` --- ## ✅ 七、总结 | 类型 | 数据来源 | 是否可选 | 是否支持默认值 | 是否支持复杂类型 | |------|----------|----------|----------------|------------------| | `@RequestParam` | 查询字符串(Query String) | ✅ | ✅ | ❌(只能用于基本类型) | | `@PathVariable` | URL 路径(Path Segment) | ❌ | ❌ | ❌(只能用于基本类型) | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值