@PathVariable注解的使用及源码解析
前言
@PathVariable 注解是我们进行JavaEE开发,最常见的几个注解之一,这篇博文我们以案例和源码相结合,帮助大家更好的了解@PathVariable 注解
使用案例
1.获取 URL 上的值
@RequestMapping("/id/{id}")
public Object getId(@PathVariable(value = "id") Integer id) {return id;
}
2.获取 URL 上的多个值
多个值以 / 分割,可以相邻也可以不相邻
2.1 多个值相邻
@GetMapping("/info/{id}/{name}")
public Object getInfo(@PathVariable(value = "id") Integer id, @PathVariable(value = "name") String name) {return "id:" + id + ",name:" + name;
}
2.1 多个值不相邻
@GetMapping("/info/{id}/delimiter/{name}")
public Object getDelimiterInfo(@PathVariable(value = "id") Integer id, @PathVariable(value = "name") String name) {return "id:" + id + ",name:" + name;
}
3. 不指定value(name)
@RequestMapping("/default/{id}")
public Object getDefaultId(@PathVariable Integer id) {return id;
}
4. 使用 Map 接受多个 URL 上的值
@GetMapping("/info_map/{id}/{name}")
public Object getInfoToMap(@PathVariable Map<String, Object> map) {StringBuilder sb = new StringBuilder();map.forEach((key, value) -> sb.append(key).append(":").append(value).append(","));return sb.substring(0, sb.length() - 1);
}
源码解析
InvocableHandlerMethod#getMethodArgumentValues
参数的处理分为两个阶段:
- 判断当前环境中存在的resolvers,是否支持解析当前参数
- 处理参数
判断是否支持解析当前参数
我的环境中存在27个resolvers,通过命名我们大概可以猜测出 PathVariableMethodArgumentResolver、PathVariableMapMethodArgumentResolver 是处理 @PathVariable 注解的 resolver
PathVariableMethodArgumentResolver#supportsParameter
@Override
public boolean supportsParameter(MethodParameter parameter) {if (!parameter.hasParameterAnnotation(PathVariable.class)) {return false;}if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);return (pathVariable != null && StringUtils.hasText(pathVariable.value()));}return true;
}
如果参数存在 @PathVariable 注解,并且指定了 value(name),则支持解析
PathVariableMapMethodArgumentResolver#supportsParameter
@Override
public boolean supportsParameter(MethodParameter parameter) {PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);return (ann != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&!StringUtils.hasText(ann.value()));
}
如果参数存在 @PathVariable 注解,并且未指定了 value(name),则支持解析
处理参数
接来下我们将重点分析 PathVariableMethodArgumentResolver 的 resolveArgument 方法,PathVariableMapMethodArgumentResolver 的 resolveArgument 方法大家可以自行阅读,相关源码如下:
大概分为以下六个步骤:
- 构建NamedValueInfo对象
- 处理Spel表达式
- 解析参数
- 处理默认值
- 类型转换
- 给 Request 域赋值
构建NamedValueInfo对象
创建NamedValueInfo对象
@PathVariable 注解的默认值是 ValueConstants.DEFAULT_NONE,并且无法手动设置
更新NamedValueInfo对象
updateNamedValueInfo 方法主要针对 @PathVariable 注解未指定 value(name)的情况,比如上文中的案例3,NamedValueInfo对象的 name 属性值为方法的参数名
处理Spel表达式
默认情况下,@PathVariable 注解是不支持解析 Spel 表达式的,我们通过源码分析一下。
如果 resolver 的 configurableBeanFactory 或 expressionContext 属性为 null ,则不进行Spel表达式的解析工作
RequestMappingHandlerAdapter#getDefaultArgumentResolvers
默认注册 PathVariableMethodArgumentResolver 使用的是无参构造方法,也就是 configurableBeanFactory 和 expressionContext 属性为 null,所以默认情况下,@PathVariable 注解不支持解析Spel表达式。可能有的小伙伴说我可以利用 WebMvcConfigurer ,自定义一个resolver,这里存在一个优先级问题,自定义的 resolver 优先级低于 mvc 手动注册的 resolver,所以一般情况下 @PathVariable 注解都不支持解析Spel表达式
我们可以看到手动注册的 resolver 优先级很低,一般情况下都是利用 mvc 内置的 resolver 进行解析
如何让 @PathVariable 注解支持解析 Spel 表达式 ?
替换内置的 PathVariableMapMethodArgumentResolver
@SpringBootApplication
@PropertySource("classpath:keys.properties")
public class BootApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(BootApplication.class);ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);List<HandlerMethodArgumentResolver> resolvers = adapter.getArgumentResolvers();if (!CollectionUtils.isEmpty(resolvers)) {try {List<HandlerMethodArgumentResolver> newResolvers = new ArrayList<>();for (HandlerMethodArgumentResolver resolver : resolvers) {if (resolver instanceof PathVariableMethodArgumentResolver) {PathVariableMethodArgumentResolver pathVariableMethodArgumentResolver = new PathVariableMethodArgumentResolver();Field factoryField = AbstractNamedValueMethodArgumentResolver.class.getDeclaredField("configurableBeanFactory");factoryField.setAccessible(true);factoryField.set(pathVariableMethodArgumentResolver, beanFactory);Field expressionField = AbstractNamedValueMethodArgumentResolver.class.getDeclaredField("expressionContext");expressionField.setAccessible(true);expressionField.set(pathVariableMethodArgumentResolver, new BeanExpressionContext(beanFactory, new RequestScope()));newResolvers.add(pathVariableMethodArgumentResolver);} else {newResolvers.add(resolver);}}adapter.setArgumentResolvers(Collections.unmodifiableList(newResolvers));} catch (Exception ignore) {}}}
}
这里我们引用了一个 keys.properties 文件,为下文中演示解析Spel表达式做准备,文件明细如下
key=a
解析 ${}
@RequestMapping("/spel_1/{a}/{b}")
public Object spel1(@PathVariable(value = "${key}") String key) {return key;
}
解析 #{}
创建 RequestKey
@Component
public class RequestKey {private String key = "b";public String getKey() {return key;}public void setKey(String key) {this.key = key;}
}
接口及响应
@RequestMapping("/spel_2/{a}/{b}")
public Object spel2(@PathVariable(value = "#{requestKey['key']}") String key) {return key;
}
参数解析
主要就是获取 URL 上的值
处理默认值
@PathVariable 注解不支持设置默认值,源码这里又处理默认值,感觉有点突兀。其实这里使用了模板模式,@RequestParam 注解的处理步骤是和 @PathVariable 注解一致的,然而 @RequestParam 注解是可以设置默认值的。我的上一篇博文写了@RequestParam 注解的使用和源码解析,有兴趣的小伙伴可以阅读一下,它们的处理流程基本是一致的,就是细节有差别。博文链接 : @RequestParam注解的使用及源码解析
类型转换
SpringBoot 会提前内置很多 convert,当存在一个 convert 可以将当前类型转换为目标类型,则会进行转换。案例演示:
创建实体类 Dog
public class Dog {private String name;public Dog(String name) {this.name = name;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +'}';}
}
创建配置类 WebConfig
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new Converter<String, Dog>() {@Overridepublic <U> Converter<String, U> andThen(Converter<? super Dog, ? extends U> after) {return Converter.super.andThen(after);}@Overridepublic Dog convert(String source) {return new Dog(source);}});}}
接口及响应
@GetMapping("/convert/{dog}")
public String getDog(@PathVariable(value = "dog") Dog dog) {return dog.toString();
}
给 Request 域赋值
案例演示
@GetMapping("/attribute/{id}")
public String getAttribute(@PathVariable(value = "id") Integer id, HttpServletRequest request) {return request.getAttribute(View.PATH_VARIABLES).toString();
}
相关文章:

@PathVariable注解的使用及源码解析
前言 PathVariable 注解是我们进行JavaEE开发,最常见的几个注解之一,这篇博文我们以案例和源码相结合,帮助大家更好的了解PathVariable 注解 使用案例 1.获取 URL 上的值 RequestMapping("/id/{id}") public Object getId(Path…...
服务器配置重点看哪些参数
对服务器有需求时,应重点考虑以下几个关键参数,以下仅供参考: 处理器(CPU):包括CPU的品牌(如Intel或AMD)、型号、核心数、线程数、主频和缓存大小。核心数越多,处理并发请…...
WSL Ubuntu 如何设置中文语言?
本章教程,主要介绍如何在WSL Ubuntu 如何设置中文语言。 操作系统:Windows 10 Pro 64 WSL子系统:Ubuntu 20.04 LTS 一、安装中文语言包 sudo apt install language-pack-zh-hans二、设置中文语言 sudo dpkg-reconfigure locales选择en_US.UTF-8 和 zh_CN.UTF-8 选择zh_CN.…...

「51媒体」政企活动媒体宣发如何做?
传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 媒体宣传加速季,100万补贴享不停,一手媒体资源,全国100城线下落地执行。详情请联系胡老师。 政企活动媒体宣发是一个系统性的过程,需要明确…...
K近邻回归原理详解及Python代码示例
K近邻回归原理详解 K近邻回归(K-Nearest Neighbors Regression, KNN)是一种基于实例的学习算法,用于解决回归问题。它通过找到输入数据点在特征空间中最相似的K个邻居(即最近的K个数据点),并使用这些邻居的…...

idea 开发工具properties文件中的中文不显示
用idea打开一个项目,配置文件propertise中的中文都不展示,如图: 可修改idea配置让中文显示: 勾选箭头指向的框即可,点击应用保存,重新打开配置文件,显示正常...

让DroidVNC-NG支持中文输入
DroidVNC-NG支持控制端输入内容,但是仅支持英文字符,如果需要控制输入法软键盘输入中文的话就没办法了,经过摸索找到了解决办法。 这个解决办法有个条件就是让DroidVNC-NG成为系统级应用(这个条件比较苛刻)ÿ…...
android dialog 显示时 activity 是否会执行 onPause onStop
当一个 Android Dialog 显示时,当前 Activity 通常不会执行 onPause 或 onStop 方法。Dialog 是附加到 Activity 上的一个窗口,它不会中断或替换当前的 Activity,因此 Activity 的生命周期方法 onPause 和 onStop 不会被调用。 然而…...

如何在MySQL中按字符串中的数字排序
在管理数据库时,我们经常遇到需要按嵌入在字符串中的数字进行排序的情况。这在实际应用中尤为常见,比如文件名、代码版本号等字段中通常包含数字,而这些数字往往是排序的关键。本文将详细介绍如何在MySQL中利用正则表达式提取字符串中的数字并…...

memcacheredis构建缓存服务器
Memcached&Redis构建缓存服务器 前言 许多Web应用都将数据保存到 RDBMS中,应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、 网站显示延迟等重大影响。Memcached/redis是高性能…...

Linux基础- 使用 Apache 服务部署静态网站
目录 零. 简介 一. linux安装Apache 二. 创建网页 三. window访问 修改了一下默认端口 到 8080 零. 简介 Apache 是世界使用排名第一的 Web 服务器软件。 它具有以下一些显著特点和优势: 开源免费:可以免费使用和修改,拥有庞大的社区支…...

接口自动化测试框架实战(Pytest+Allure+Excel)
🍅 视频学习:文末有免费的配套视频可观看 🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 1. Allure 简介 Allure 框架是一个灵活的、轻量级的、支持多语言的测试报告工具,它不…...

如何预防和处理他人盗用IP地址?
IP地址的定义及作用 解释 IP 地址在互联网中的作用。它是唯一标识网络设备的数字地址,类似于物理世界中的邮政地址。 1、IP地址盗窃的定义 解释一下什么是IP地址盗用,即非法使用他人的IP地址或者伪造IP地址的行为,这种行为可能引发法律和安…...

【ai】李沐 动手深度学学v2 环境安装:anaconda3、pycharm、d2
cuda-toolkit cuda_12.5.0_windows_network.exe 官方课程网站 第二版资源下载release版本 pycharm版本 李沐 【动手学深度学习v2 PyTorch版】 课程笔记 CUDA 选择11, 实际下载 12.5.0...
前后端分离对软件行业及架构设计的影响
在软件开发领域,前后端分离是一种越来越流行的架构设计模式。这种方法将用户界面(前端)与服务器逻辑(后端)分离开来,允许它们独立开发、测试和部署。本文将探讨前后端分离对软件行业和架构设计的影响&#…...
深入解析Dubbo架构层次
什么是Dubbo? Dubbo是阿里巴巴开源的一款高性能优秀的服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。它的主要功能包括: 远程通信:提供高效的远程通信能力。负载均衡࿱…...
关于GPIO的上拉、下拉,无上下拉
1.GPIO_PULLUP(上拉) 作用和原理 作用:上拉模式会在GPIO引脚和电源电压(Vcc)之间连接一个内部上拉电阻。原理:当引脚配置为输入模式时,如果引脚没有连接到其他外部电路,内部上拉电…...

Python 语法基础二
7.常用内置函数 执行这个命令可以查看所有内置函数和内置对象(两个下划线) >>>dir(__builtins__) [__class__, __contains__, __delattr__, __delitem__, __dir__, __doc__, __eq__, __format__, __ge__, __getattribute__, __getitem__, __gt…...
HTML5与HTML:不仅仅是标签的革新
当我们提到HTML5,很多人会想到这是HTML的一个升级版本,增加了许多新的标签和特性。然而,HTML5带来的变化远不止于此。它是一个全面的网页开发框架,重新定义了网络应用程序的构建方式,为开发者提供了前所未有的灵活性和…...
Mybatis面试学习
1.介绍一下mybatis mybatis是一个半自动的ORM的框架,ORM就是对象关系映射。(对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系)…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...