Spring数据类型转化

news2024/9/22 1:43:24

HTTP请求中携带的queryString和form-data数据(文件除外)都是是String类型。那么在Controller上怎么可以直接指定数据类型呢。其实是Spring默认帮我们做了类型转化。

内置数据类型转换器介绍

Converter<S, T>

  1. String -> Integer
    @GetMapping("/age")
    @ResponseBody
    public String age(Integer age) {
        System.out.println(age);
        return "OK";
    }

例如这个接口,我们用Postman发送数据,如图:
在这里插入图片描述
解析过程中String转化成了Integer
在这里插入图片描述
从convertIfNecessary追进去,发现最终使用的是 org.springframework.core.convert.support.GenericConversionService.ConverterFactoryAdapter,它内部对org.springframework.core.convert.converter.Converter进行了代理。
我们看org.springframework.core.convert.converter.Converter都有哪些实现类,看到有一个org.springframework.core.convert.support.StringToNumberConverterFactory.StringToNumber,看它的convert方法

	private static final class StringToNumber<T extends Number> implements Converter<String, T> {
		// 具体的数字类,integer、Float、BigDecimal等
		private final Class<T> targetType;

		public StringToNumber(Class<T> targetType) {
			this.targetType = targetType;
		}
		@Override
		@Nullable
		public T convert(String source) {
			if (source.isEmpty()) {
				return null;
			}
			// 根据具体数字类型做转换
			return NumberUtils.parseNumber(source, this.targetType);
		}
	}

因为数字类型有很多种,如integer、Float、BigDecimal等,它们都继承自Number类,所以StringToNumber的泛型使用的是Number,而不是具体的Integer、Float。使用时,根据内部属性targetType判断要创建什么类型的转换器。需要注意像这种工厂形式转换器,Spring并没有缓存具体的转换器对象,只是缓存了工厂对象。
2. String -> Enum
假设我们有一个枚举类

public enum Gender {
    FEMAL(1, "女性"),
    MALE(2, "男性")
    ;
    private int code;
    private String desc;
    Gender(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    
    public Integer getCode() {
        return code;
    }
}

我们的Controller如下

   @GetMapping("/gender")
    @ResponseBody
    public String gender(@RequestParam Gender gender) {
        System.out.println(gender);
        return "OK";
    }

类似上面的套路我们发现有一个org.springframework.core.convert.support.StringToEnumConverterFactory类,内部有一个StringToEnum静态类。转化过程是用到了Enum类自身带的方法。

		@Override
		@Nullable
		public T convert(String source) {
			if (source.isEmpty()) {
				// It's an empty enum identifier: reset the enum value to null.
				return null;
			}
			return (T) Enum.valueOf(this.enumType, source.trim());
		}

在这里插入图片描述
此时,String类型的参数就能转化成Enum。

我们知道Enum不仅有String类型的name属性,还有int类型的ordinal,哪是不是也有一个类似IntToEnum的转换器呢,找一下,确实有org.springframework.core.convert.support.IntegerToEnumConverterFactory,是根据Enum的ordinal转换成Enum。但是注意,Postman传的参数都是String类型的,不会用到这个IntegerToEnumConverterFactory,也就是说如果像下面这样传参数是不能正确转换的:
在这里插入图片描述
所以我们需要自己写一个转换器,把字符串格式的数字转成枚举

public class MyIntegerToGender implements Converter<String, Gender> {
    @Override
    public Gender convert(String source) {
        final Gender[] values = Gender.values();
        for (int i = 0; i < values.length; i++) {
            if (values[i].getCode() == Integer.valueOf(source)) {
                return values[i];
            }
        }
        return null;
    }
}

在SpringBoot环境下,只需要把MyIntegerToGender 注册成Bean即可生效,在SpringMVC环境下需要如下配置:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new MyIntegerToGender());
    }
}

此时就可以把前端传递的数字格式的参数转成枚举了。
我们发现还有一个StringToBooleanConverter,里面对String和Boolean的映射做了一些约定,这就是为什么我们输入no字符串,得到的Boolean却是false。

		trueValues.add("true");
		trueValues.add("on");
		trueValues.add("yes");
		trueValues.add("1");

		falseValues.add("false");
		falseValues.add("off");
		falseValues.add("no");
		falseValues.add("0");

GenericConverter

如果我想实现String和List,String和Set之间的转换,用Converter<S, T>就不太合适了,Converter<S, T>可以进行两种确定类型之间的转换,但不适合带泛型的集合,毕竟集合中的元素甚至都可以不同。
GenericConverter与Converter相比有两点不同

  1. 支持集合、数组之间互转
  2. 能够拿到source和target的类信息之外的信息,如注解信息

支持集合、数组之间互转

    @GetMapping("/listStr")
    @ResponseBody
    public String listStr(@RequestParam List<String> strings) {
        strings.forEach(System.out::println);
        return "ok";
    }

在这里插入图片描述
在这里插入图片描述
可以看到成功以集合的形式接收。
GenericConverter底层依赖的还是Converter,一般 GenericConverter的实现类中都有一个ConversionService对象,这个对象持有所有Converter, GenericConverter的转换工作,最终通过ConversionService转发给了具体的Converter。比如我们只添加了MyIntegerToGender 用于单个枚举类型的转换,如果此时我们这样写,也会成功赋值:

    @GetMapping("/listGender")
    @ResponseBody
    public String listGender(@RequestParam List<Gender> genders) {// 用集合接收参数
        genders.forEach(System.out::println);
        return "ok";
    }

在这里插入图片描述

能够拿到source和target的类信息之外的信息,如注解信息

因为从HTTP请求中QueryString 、 form-data、x-www-form-urlencoded中解析出的原始数据都是String类型的,默认是没有StringToDate的转换器的,但是我们可以这样接收Date参数:

    @GetMapping("/date")
    @ResponseBody
    public String date(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
        System.out.println(date.getTime());
        return "OK";
    }

在这里插入图片描述
如果不带@DateTimeFormat注解会报错。Spring在根据源类型和目标类型查找合适转换器时,会参考参数上的注解信息。这里使用的是ParserConverter,它内部有一个Parser,负责把String按一定的格式输出。有小伙伴一直纠结Converter和Formatter的区别,其实Formatter = Converter + 格式。即按照一定的格式转换。
3. String -> 文件
按照上面的套路,如果有这么一个接口,我们可能会想到,使用的是StringToFileConverter.

    @GetMapping("/file")
    @ResponseBody
    public String file(@RequestParam File file) {
        System.out.println(file);
        return "ok";
    }

在这里插入图片描述

确实有这么一个转换器,但它是Spring内部使用的,没有暴露给我我们。这个接口使用的其实是ObjectToObjectConverter。这个转化器会根据sourceClass和targetClass,查找有没有方法或者构造函数能让source变成target。
在这里插入图片描述

题外话

@RequestParam和@RequestPart都可以接收MultipartFile,前者是走Converter流程,后者是走HttpMessageConverter流程。当方法中直接用MultipartFile作为参数时,两个注解都可以正确接收,因为此时不需要数据类型转化。但是如果用String接收,@RequestParam就会接收失败,因为没有MultipartFile和String之间的转换器,但是@RequestPart可以使用String类型接收,因为StringHttpMessageConverter做了MultipartFile和String之间的转换。

小结

本文介绍了@RequestParam注解下常见的数据格式转换,这些转换大部分是Spring默认帮我们做的,使用者几乎是无感知的,但是有时当我们会遇到需要自定义的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2094530.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++ 146 三目运算符

const修饰指向的内存你空间不能被修改 c语言中的const冒牌货 符号表c 卸载 undief

AWTK 1.8 发布

1.8 版本更新 1. 细节完善 大量细节完善请参考 最新动态 2. 新增文档 拖入文件事件如何使用 packed image如何自定义资源加载方式如何使用 CMake 构建 AWTK 应用如何将资源编译到应用程序并使用它们关于自定义控件的 offset 的使用注意事项 3. 新增重要特性 使用 svgtiny 解…

斑马线识别检测系统源码分享

斑马线识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

MAC 、 IP ARP

MAC地址 基本概念 MAC地址是以太网的MAC子层所使用的地址——数据链路层 使用点对点信道的数据链路层不需要使用地址 使用广播信道的数据链路层必须使用地址来区分各主机 实现同一个广播信道上的不同主机之间的通信 每个主机都必须要有一个唯一的表示——一个数据链路层地址…

【计算机网络】应用层HTTP协议

我们已经实现过应用层协议&#xff0c;但也要看一看成熟的应用层协议 目录 1 HTTP协议11 URL12 urlencode 和 urldecode13 HTTP 协议请求与响应格式请求格式响应格式 14 界面的基本处理显示基本主页显示图片页面跳转 15 常见header16 状态码161 404举例162 关于3开头的状态码 1…

JavaEE 第20节 用TCP套接字实现简单回显服务器

这里写目录标题 一、API介绍ServerSocketSocket 二、创建简单的回显服务器服务器端客户端 一、API介绍 ServerSocket 构造方法 方法签名方法说明ServerSocket(int port)创建⼀个服务端流套接字Socket&#xff0c;并绑定到指定端⼝ 关于此构造方法的注意事项&#xff1a; Ser…

栈和队列的习题详解(1):有效的括号

前言&#xff1a; 在差不多二十天前小编写过栈和队列的详解&#xff0c;本来我想当时写完那两个结构之后就继续写它们的习题&#xff0c;但是写完那几篇博客以后&#xff0c;我就开始狂玩了十几天&#xff0c;我在上篇博客也说过&#xff0c;导致我在刚开学的时候就忘记了这个习…

mac的使用

mac使用python的问题 对于python的虚拟环境&#xff0c;其实是基于已经安装到本地的python来安装不同的包。&#xff08;之前我的mac上只安装了python3.9.6 &#xff0c;安装的位置为/usr/bin/python3&#xff09;然后我在vscode里怎么找都找不到如何弄一个python3.7.6 的版本…

使用Pywin32和其他库控制Office软件进行自动化操作

目录 引言 Pywin32概述 基本概念 安装与配置 基本使用 Word自动化操作 文档创建与编辑 文档格式化 宏的运行 PowerPoint自动化操作 演示文稿的创建与编辑 幻灯片内容的格式化 高级应用&#xff1a;从Word自动生成PPT 读取Word文档中的内容。 保存生成的PowerPoi…

NetSuite AI 图生代码

去年的ChatGPT热潮期间&#xff0c;我们写过一篇文章说GTP辅助编程的事。 NetSuite GPT的辅助编程实践_如何打开netsuite: html script notes的视图-CSDN博客文章浏览阅读2.2k次&#xff0c;点赞4次&#xff0c;收藏3次。作为GPT综合症的一种表现&#xff0c;我们今朝来探究下…

SOMEIP_ETS_076: Wrong_Method_ID

测试目的&#xff1a; 验证当设备&#xff08;DUT&#xff09;接收到一个包含错误方法ID的SOME/IP请求时&#xff0c;是否能够返回错误消息或忽略该请求。 描述 本测试用例旨在检查DUT在处理一个echoUINT8方法的SOME/IP消息时&#xff0c;如果消息中包含的方法ID不正确&…

NC 寻找峰值

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 给定一个长度…

12:以太网口模块布局

1.以太网口布局重点 ①两对差分线对应百兆网口&#xff0c;4对差分线对应千兆网口 ②以太网口重点是处理2对差分线&#xff0c;且优先走顶层 2.抽头信号要加粗&#xff1a;20mil-30mil

Unity学习路线

目录 一、Unity官方推荐路线二、AI总结的学习路线1、Unity学习路线图&#xff08;文言一心&#xff09;一、基础入门&#xff08;初级&#xff09;二、进阶提升&#xff08;中级&#xff09;三、高级深入&#xff08;高级&#xff09;四、专家级探索 注意事项 2、Unity学习路线…

【例003】利用MATLAB绘制有趣平面图形

题目&#xff1a; 用 ezplot 画出由方程 sin ⁡ ( x 2 m y 2 1000 ) cos ⁡ ( x y ) \sin(x^2\frac{my^2}{1000})\cos(xy) sin(x21000my2​)cos(xy) 确定隐函数的图形。 求解&#xff1a; 我们分别取m为100&#xff0c;1000,10000不同的值&#xff0c;绘制不同情况下的图…

计算机毕业设计选题推荐-公司考勤管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

解决银河麒麟中`netstat`命令不可用

解决银河麒麟中netstat命令不可用 1、问题2、解决方案3、 总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、问题 在银河麒麟服务器操作系统中&#xff0c;netstat命令可能不可用&#xff0c;因为它属于未安装的net-tools软件包。 2…

LabVIEW程序员的护城河是什么

LabVIEW程序员的护城河在于他们深厚的行业经验和对特定领域的深刻理解&#xff0c;这使得他们在工业自动化、测试与测量等领域中难以被轻易取代。然而&#xff0c;随着AI技术的进步&#xff0c;部分基础性和重复性的工作可能会逐渐被AI接管&#xff0c;但LabVIEW程序员的独特技…

WEB渗透Win提权篇-合集(上)

完整20w字笔记&#xff1a; 夸克网盘分享 AppLocker GPO HKLM\SOFTWARE\Policies\Microsoft\Windows\SrpV2&#xff08;keys&#xff1a;Appx、Dll、Exe、Msi 和脚本&#xff09;。 列出 AppLocker 规则 PowerView PS C:\> Get-AppLockerPolicy -Effective | select -Expa…