springboot系列十: 自定义转换器,处理JSON,内容协商

文章目录

  • 自定义转换器
    • 基本介绍
    • 应用实例
    • 查看源码
    • 注意事项和细节
  • 处理JSON
    • 需求说明
    • 应用实例
  • 内容协商
    • 基本介绍
    • 应用实例
    • debug源码
    • 优先返回xml
    • 注意事项和细节

在这里插入图片描述


⬅️ 上一篇: springboot系列九: 接收参数相关注解


🎉 欢迎来到 springboot系列十: 自定义转换器,处理JSON,内容协商 🎉

在本篇文章中,我们将探讨如何在 Spring Boot 中实现自定义转换器、处理 JSON 数据以及进行内容协商。通过掌握这些技术,您可以更灵活地处理不同格式的数据,提高应用程序的兼容性和用户体验。


🔧 本篇需要用到的项目:


自定义转换器

基本介绍

1.SpringBoot在响应客户端请求时, 将提交的数据封装成对象时, 使用了内置转换器

2.SpringBoot也支持自定义转换器, 这个内置转换器在debug的时候, 可以看到, 提供了124个内置转换器, 看下源码. GenericConverter类-ConvertiblePair(内部类)

在这里插入图片描述

在这里插入图片描述]

应用实例

需求说明: 演示自定义转换器使用

1.修改save.html

<!--使用自定义转换器关联car, 字符串整体提交, 使用,号间隔-->
坐骑: <input name="name" value="碧水金睛兽,666.6"><br/

2.创建src/main/java/com/zzw/springboot/config/WebConfig.java, 增加自定义转换器
springboot系列四: sprintboot容器功能

/*** @Configuration(proxyBeanMethods = false)* 1.表示 WebConfig 是一个配置类* 2.proxyBeanMethods = false 表示使用Lite模式*/
@Configuration(proxyBeanMethods = false)
public class WebConfig {//注入bean WebMvcConfiger@Beanpublic WebMvcConfigurer webMvcConfigurer() {//整个是WebMvcConfigurer接口的匿名内部类return new WebMvcConfigurer() {@Overridepublic void addFormatters(FormatterRegistry registry) {/*** 解读* 1.在addFormatters方法中, 增加一个自定义转换器* 2.增加自定义转换器 String -> Car* 3.增加的自定义转换器会注册到 converters 容器中* 4.converters 底层结构是 ConcurrentHashMap, 内置有124个转换器[不同版本个数不一样~]* 5.一会我们debug查看这些转换器*/registry.addConverter(new Converter<String, Car>() {//传入Converter接口的匿名内部类@Overridepublic Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6//这里加入自定义的转换业务代码if (!ObjectUtils.isEmpty(source)) {String[] split = source.split(",");Car car = new Car();car.setVehicleName(split[0]);car.setVehiclePrice(Double.parseDouble(split[1]));return car;}return null;}});}};}
}

3.测试
monster = Monster(id=100, name=张三, age=30, maritalStatus=false, birthday=Sat Jan 01 00:00:00 CST 1994, car=Car(vehicleName=碧水金睛兽, vehiclePrice=666.6))

查看源码

1.debug, 可以看到我们新增的Converter

在这里插入图片描述
在这里插入图片描述

快捷键查看有多少元素

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

注意事项和细节

1.注册转换器换种写法, 方便理解

/*** @Configuration(proxyBeanMethods = false)* 1.表示 WebConfig 是一个配置类* 2.proxyBeanMethods = false 表示使用Lite模式*/
@Configuration(proxyBeanMethods = false)
public class WebConfig {//注入bean WebMvcConfiger@Beanpublic WebMvcConfigurer webMvcConfigurer() {//整个是WebMvcConfigurer接口的匿名内部类return new WebMvcConfigurer() {@Overridepublic void addFormatters(FormatterRegistry registry) {/*** 解读* 1.在addFormatters方法中, 增加一个自定义转换器* 2.增加自定义转换器 String -> Car* 3.增加的自定义转换器会注册到 converters 容器中* 4.converters 底层结构是 ConcurrentHashMap, 内置有124个转换器[不同版本个数不一样~]* 5.一会我们debug查看这些转换器*//*registry.addConverter(new Converter<String, Car>() {//传入Converter接口的匿名内部类@Overridepublic Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6//这里加入自定义的转换业务代码if (!ObjectUtils.isEmpty(source)) {String[] split = source.split(",");Car car = new Car();car.setVehicleName(split[0]);car.setVehiclePrice(Double.parseDouble(split[1]));return car;}return null;}});*///换种写法注册自定义转换器, 方便理解//1.先创建一个自定义的转换器Converter<String, Car> zzwConverter = new Converter<String, Car>() {//传入Converter接口的匿名内部类@Overridepublic Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6//这里加入自定义的转换业务代码if (!ObjectUtils.isEmpty(source)) {String[] split = source.split(",");Car car = new Car();car.setVehicleName(split[0]);car.setVehiclePrice(Double.parseDouble(split[1]));return car;}return null;}};//添加转换器到converters容器registry.addConverter(zzwConverter);//还可以增加更多的转换器....}};}
}

2.假如我们不添加自定义转换器, 会报typeMismatch错误, 报400错误. 而400错误是客户端的错误, 请求包含语法错误.
JavaWeb系列八: WEB 开发通信协议(HTTP协议)

在这里插入图片描述

3.创建两个自定义转换器

/*** @Configuration(proxyBeanMethods = false)* 1.表示 WebConfig 是一个配置类* 2.proxyBeanMethods = false 表示使用Lite模式*/
@Configuration(proxyBeanMethods = false)
public class WebConfig {//注入bean WebMvcConfiger@Beanpublic WebMvcConfigurer webMvcConfigurer() {//整个是WebMvcConfigurer接口的匿名内部类return new WebMvcConfigurer() {@Overridepublic void addFormatters(FormatterRegistry registry) {/*** 解读* 1.在addFormatters方法中, 增加一个自定义转换器* 2.增加自定义转换器 String -> Car* 3.增加的自定义转换器会注册到 converters 容器中* 4.converters 底层结构是 ConcurrentHashMap, 内置有124个转换器[不同版本个数不一样~]* 5.一会我们debug查看这些转换器*//*registry.addConverter(new Converter<String, Car>() {//传入Converter接口的匿名内部类@Overridepublic Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6//这里加入自定义的转换业务代码if (!ObjectUtils.isEmpty(source)) {String[] split = source.split(",");Car car = new Car();car.setVehicleName(split[0]);car.setVehiclePrice(Double.parseDouble(split[1]));return car;}return null;}});*///换种写法注册自定义转换器, 方便理解//1.先创建一个自定义的转换器Converter<String, Car> zzwConverter = new Converter<String, Car>() {//传入Converter接口的匿名内部类@Overridepublic Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6//这里加入自定义的转换业务代码if (!ObjectUtils.isEmpty(source)) {String[] split = source.split(",");Car car = new Car();car.setVehicleName(split[0]);car.setVehiclePrice(Double.parseDouble(split[1]));return car;}return null;}};//还可以增加更多的转换器....//第2个自定义的转换器Converter<String, Monster> zzwConverter2 = new Converter<String, Monster>() {@Overridepublic Monster convert(String source) {return null;}};//添加转换器到converters容器registry.addConverter(zzwConverter);registry.addConverter(zzwConverter2);}};}
}

debug, 看一看 converters容器 是不是变成了 126 个.

在这里插入图片描述
在这里插入图片描述

4.创建三个自定义转换器, 由于key是[源类型->目标类型], 所以会覆盖掉一个.

//1.先创建一个自定义的转换器
Converter<String, Car> zzwConverter = new Converter<String, Car>() {//传入Converter接口的匿名内部类@Overridepublic Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6//这里加入自定义的转换业务代码if (!ObjectUtils.isEmpty(source)) {String[] split = source.split(",");Car car = new Car();car.setVehicleName(split[0]);car.setVehiclePrice(Double.parseDouble(split[1]));return car;}return null;}
};//还可以增加更多的转换器....
//第2个自定义的转换器
Converter<String, Monster> zzwConverter2 = new Converter<String, Monster>() {@Overridepublic Monster convert(String source) {return null;}
};
//第3个自定义的转换器
Converter<String, Car> zzwConverter3 = new Converter<String, Car>() {@Overridepublic Car convert(String source) {return null;}
};//添加转换器到converters容器
registry.addConverter(zzwConverter);
registry.addConverter(zzwConverter2);
registry.addConverter(zzwConverter3);

1)测试, 是否覆盖.

在这里插入图片描述
在这里插入图片描述

2)查看 converters 容器. 因为第三个转换器和第一个转换器 key 是相同的, 所以覆盖掉了.

在这里插入图片描述

处理JSON

需求说明

演示返回JSON格式的数据

应用实例

1.SpringBoot 支持返回 JSON 格式数据, 在启用WEB开发场景时, 已经引入了相关依赖.
springboot系列二: sprintboot依赖管理

在这里插入图片描述
在这里插入图片描述

2.新建src/main/java/com/zzw/springboot/controller/ResponseController.java

@Controller
public class ResponseController {//编写方法, 返回monster数据 要求以json格式返回@GetMapping(value = "/getMonster")@ResponseBodypublic Monster getMonster() {//说明//开发中 monster对象是从db获取,这里我们模拟一个mosnter对象Monster monster = new Monster();monster.setId(100);monster.setName("张三");monster.setAge(18);monster.setBirthday(new Date());monster.setMaritalStatus(false);Car car = new Car();car.setVehicleName("奔驰");car.setVehiclePrice(100000.0);monster.setCar(car);return monster;}
}

3.Postman测试.

在这里插入图片描述

4.Debug一下 monster对象如何以Json格式返回.

浏览器/Postman 请求, 第一个断点

在这里插入图片描述

第二个断点, 找到 AbstractJackson2HttpMessageConverter.class

在这里插入图片描述

在这里插入图片描述

用工厂模式创建了个 generator.

在这里插入图片描述

generator是 UTF8JsonGenerator

在这里插入图片描述

object 就是 monster对象

在这里插入图片描述

这条语句一旦执行完毕, 浏览器就拿到数据.

在这里插入图片描述

内容协商

基本介绍

1.根据客户端接收能力不同, SpringBoot返回不同媒体类型的数据.
JavaWeb系列八: WEB 开发通信协议(HTTP协议)

2.比如:
客户端Http请求, 携带 Accept aaplication/xml 请求头, 要求服务端返回xml数据;
客户端Http请求, 携带 Accept aaplication/json 请求头, 则要求服务端返回json数据

3.效果如下
如果Accept, 我设置的是 application/json, 那么返回的数据就是 json 格式.
在这里插入图片描述

如果Accept, 我设置的是 application/xml, 那么返回的数据就是 xml 格式.

在这里插入图片描述

应用实例

需求说明: 使用Postman发送Http请求, 根据请求头不同, 返回对应的json数据, 或者xml数据, 如图

在这里插入图片描述

注意: Accept: */* 默认返回 json 格式

在这里插入图片描述

在底层,generator生成的是xml格式的, 但是在进行转换的时候, 需要有一个jar包的依赖.

<!--引入处理xml的依赖-->
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.12.4</version>
</dependency>

debug源码

Postman切换不同的Accept类型, 来Debug源码, 看看对应的JsonGenerator类型

1,返回json类型

在这里插入图片描述

contentType进行内容协商

在这里插入图片描述

在这里插入图片描述

2.返回xml类型

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

优先返回xml

加入xml依赖以后, 使用浏览器请求,为什么会返回xml数据, 而不是json?

分析
(1)浏览器请求后, 后端接收到的contentType值是 application/xhtml+xml, 为什么?

(2)因为请求头信息, 如下
1.Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8

2.application/xhtml+xml 的权重比较高0.9, 后面的类型, 包括 */* 的权重是 0.8

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意事项和细节

1.Postman可以通过修改Accept的值, 来访会不同的数据格式.

2.对于浏览器, 我们无法修改其Accept的值, 怎么办? 解决方案: 开启基于请求参数的内容协商功能.

1)修改application.yml, 开启基于请求参数的内容协商功能.

spring:mvc:contentnegotiation:favor-parameter: true #开启基于请求参数的内容协商功能

在这里插入图片描述

2)浏览器测试

在这里插入图片描述

3)注意, 参数format是规定好的, 在开启请求参数的内容协商功能后, SpringBoot底层ParameterContentNegotiationStrategy会通过format来接收参数, 然后返回对应的媒体类型/数据格式, 当然format=xx这个xx 媒体类型/数据格式 是SpringBoot可以处理的才行, 不能乱写.

在这里插入图片描述

4)修改parameterName

spring:mvc:contentnegotiation:favor-parameter: true #开启基于请求参数的内容协商功能parameter-name: helloFormat #指定一个内容协商的参数名

5)测试

在这里插入图片描述


🔜 下一篇预告: [即将更新,敬请期待]


📚 目录导航 📚

  1. springboot系列一: springboot初步入门
  2. springboot系列二: sprintboot依赖管理
  3. springboot系列三: sprintboot自动配置
  4. springboot系列四: sprintboot容器功能
  5. springboot系列五: springboot底层机制实现 上
  6. springboot系列六: springboot底层机制实现 下
  7. springboot系列七: Lombok注解,Spring Initializr,yaml语法
  8. springboot系列八: springboot静态资源访问,Rest风格请求处理
  9. springboot系列九: 接收参数相关注解
  10. springboot系列十: 自定义转换器,处理JSON,内容协商

💬 读者互动 💬
在学习 Spring Boot 自定义转换器、处理 JSON 及内容协商的过程中,您有哪些新的发现或疑问?欢迎在评论区留言,让我们一起讨论吧!😊


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

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

相关文章

ssh远程登录另一台linux电脑

大部分的博客内容所说的安装好ssh服务后&#xff0c;terminal输入 ssh -p port_number clientnameserver_ip 之后输入密码等等就可以登上别人的电脑 但是这是有一个前提的&#xff0c;就是这两台电脑要在同一个局域网下面。 如果很远呢&#xff1f; 远到不在同一个网下面怎么办…

1.JavaWeb开发简介(Tomcat安装使用+Servlet简介)

文章目录 一.web开发简介1.概念:2.特点:3.常用技术:4.服务架构5.web应用开发模式6.HTTP协议1)概念:2)HTTP最基本的过程是:3)IP/域名4)HTTP协议请求方式 7.JavaWeb的相关技术8.Java Web服务器 二、安装配置Tomcat1.简介2.Tomcat目录结构 三.Servlet的入门应用1.使用步骤2.使用注…

ABAP使用SQL直接更新数据库与使用IN UPDATE TASK的区别

1. 背景 刚接触ABAP的小伙伴常常会有这样的疑问&#xff0c;为什么不直接使用Open SQL直接更新数据库&#xff0c;而要把对DB的操作封装到IN UPDATE TASK中呢&#xff1f; 对于这个问题&#xff0c;比较常见的解释是&#xff0c;IN UPDATE TASK的方式会保证数据更新的一致性。…

接口开发:Orcal数据库的批量修改sql

场景&#xff1a;在日常的CURD中一定会用到批量修改。在我们的项目中&#xff0c;使用的数据库是Orcal&#xff0c;由于之前基本都是使用Mysql的&#xff0c;使用的sql语句也基本都是用mysql的。但是在这次的接口编写时用mysql的批量修改出了问题&#xff0c;刚开始我还以为是写…

如何每天不用动手就可以自动加人

只需要设置一次&#xff0c;批量导入客户号码或是微信号并设置好添加规则&#xff0c;系统就会自动进行添加。

Elastic 线下 Meetup 将于 2024 年 7 月 27 号在深圳举办

2024 Elastic Meetup 深圳站活动&#xff0c;由 Elastic、腾讯、新智锦绣联合举办&#xff0c;现诚邀广大技术爱好者及开发者参加。 时间地点 2024年 7 月 27 日 13:30-18:00 活动地点 中国深圳 南山区海天二路 33 号腾讯滨海大厦 北塔 3 楼多功能厅 ​ 活动流程 14:00-15…

HTTP请求走私漏洞原理与利用手段分析

文章目录 前言Http请求走私1.1 漏洞诞生场景1.2 漏洞基本原理1.3 HTTP1.1与2.0 请求走私分类2.1 CL.TE类型实例2.2 TE.CL类型实例2.3 TE.TE混淆实例2.4 漏洞检测工具&#xff1f; 请求走私利用3.1 绕过前端安全控制3.2 揭示前端请求重写3.3 捕获他人请求内容3.4 走私构造反射XS…

从数据湖到湖仓一体:统一数据架构演进之路

文章目录 一、前言二、什么是湖仓一体&#xff1f;起源概述 三、为什么要构建湖仓一体&#xff1f;1. 成本角度2. 技术角度 四、湖仓一体实践过程阶段一&#xff1a;摸索阶段(仓、湖并行建设)阶段二&#xff1a;发展阶段方式一、湖上建仓(湖在下、仓在上)方式二&#xff1a;仓外…

MySQL运维实战之Clone插件(10.1)使用Clone插件

作者&#xff1a;俊达 clone插件介绍 mysql 8.0.17版本引入了clone插件。使用clone插件可以对本地l或远程的mysql实例进行clone操作。clone插件会拷贝innodb存储引擎表&#xff0c;clone得到的是原数据库的一个一致性的快照&#xff0c;可以使用该快照数据来启动新的实例。cl…

SpringMVC注解全解析:构建高效Web应用的终极指南 (下)

一. 引言 在上篇文章中&#xff0c;我们介绍了几个重要的SpringMVC注解&#xff1a; SpringBootApplication&#xff1a;Spring Boot项目的启动类注解。RequestMapping&#xff1a;用于映射URL到控制器类或方法&#xff0c;支持多种请求方式。RequestParam&#xff1a;用于绑…

UniVue@v1.5.0版本发布:里程碑版本

前言 以后使用UniVue都推荐使用1.5.0以后的版本&#xff0c;这个版本之后&#xff0c;更新的速度将会放缓。 希望这个框架能够切实的帮助大家更好的开发游戏&#xff0c;做出一款好游戏&#xff01;本开源项目采用的开源协议为MIT协议&#xff0c;完全开源化&#xff0c;以后也…

基于Python+Django+MySQL的心理咨询预约系统

心理咨询预约系统 DjangoMySQL 基于PythonDjangoMySQL的心理咨询预约系统 项目主要依赖Django3.2&#xff0c;MySQL 支持随机验证码生成与登录验证 简介 基于PythonDjangoMySQL的心理咨询预约系统通过连接数据库获取数据&#xff0c;登录新增随机数字验证码验证。具体可以看…

【机器学习】机器学习与图像分类的融合应用与性能优化新探索

文章目录 引言第一章&#xff1a;机器学习在图像分类中的应用1.1 数据预处理1.1.1 数据清洗1.1.2 数据归一化1.1.3 数据增强 1.2 模型选择1.2.1 卷积神经网络1.2.2 迁移学习1.2.3 混合模型 1.3 模型训练1.3.1 梯度下降1.3.2 随机梯度下降1.3.3 Adam优化器 1.4 模型评估与性能优…

Qt支持LG高级汽车内容平台

Qt Group与LG 电子&#xff08;简称LG&#xff09;正携手合作&#xff0c;将Qt软件框架嵌入其基于 webOS的ACPLG车载娱乐平台&#xff0c;用于应用程序开发。该合作旨在让原始设备制造商&#xff08;OEM&#xff09;的开发者和设计师能为汽车创建更具创新性的沉浸式汽车内容流媒…

【深度学习】inpaint图像中的alpha混合图的边缘处理

比如原图是&#xff1a; 红圈内就是文字水印&#xff0c;经过inpaint后得到图和原图混合&#xff0c;如何处理边界呢&#xff0c;这个代码可以干这事&#xff1a; 越是中心就直接用inpaint图&#xff0c;否则就用原图&#xff0c;这样进行alpha混合。 import numpy as np i…

uniapp:国家、省市区,4级联动

使用uview的Select 列选择器 选择器完成国家&#xff0c;省市区&#xff0c;4级联动 要求后台数据格式&#xff1a; list: [{label: 中国,value: 1,children: [{label: 河南省,value: 2,children: [{label: 郑州市,value: 3,children: [{label: 中原区,value: 4},{label: 郑东…

Re:从零开始的C++世界——类和对象(下)

文章目录 前言1.再谈构造函数&#x1f34e;构造函数体赋值&#x1f34e;初始化列表&#x1f34e;特性&#x1f34c;特性一&#x1f34c;特性二&#x1f34c;特性三&#x1f34c;特性四&#x1f34c;特性五 &#x1f34e;explicit 关键字 2.static成员&#x1f34e;概念&#x1…

2.4G收发芯片XL2412P,集成单片机 ,通信距离可达110米

XL2412P芯片是一款高性能低功耗的SOC集成无线收发芯片&#xff0c;由RF芯片和32位M0核MCU集成&#xff0c;工作在2.400~2.483GHz世界通用ISM频段。XL2412P为SSOP16封装&#xff0c;正常工作电压范围&#xff1a;1.7~3.6V&#xff0c;正常工作温度范围-40~85℃。XL2412P发射输出…

MQTT服务端EMQX开源版安装和客户端MQTTX介绍

一、EMQX是什么 EMQX 是一款开源的大规模分布式 MQTT 消息服务器&#xff0c;功能丰富&#xff0c;专为物联网和实时通信应用而设计。EMQX 5.0 单集群支持 MQTT 并发连接数高达 1 亿条&#xff0c;单服务器的传输与处理吞吐量可达每秒百万级 MQTT 消息&#xff0c;同时保证毫秒…

vscode 中python 支持自动跳转

随笔记录 目录 1. 背景介绍 2. 解决方案 1. 背景介绍 vscode 远程ssh 打开python 脚本无法自动跳转 2. 解决方案 安装python 插件即可。 至此&#xff0c;已完成vscode 上py 文件支持自动跳转功能