文章目录
- 自定义转换器
- 基本介绍
- 应用实例
- 查看源码
- 注意事项和细节
- 处理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)测试
🔜 下一篇预告: [即将更新,敬请期待]
📚 目录导航 📚
- springboot系列一: springboot初步入门
- springboot系列二: sprintboot依赖管理
- springboot系列三: sprintboot自动配置
- springboot系列四: sprintboot容器功能
- springboot系列五: springboot底层机制实现 上
- springboot系列六: springboot底层机制实现 下
- springboot系列七: Lombok注解,Spring Initializr,yaml语法
- springboot系列八: springboot静态资源访问,Rest风格请求处理
- springboot系列九: 接收参数相关注解
- springboot系列十: 自定义转换器,处理JSON,内容协商
💬 读者互动 💬
在学习 Spring Boot 自定义转换器、处理 JSON 及内容协商的过程中,您有哪些新的发现或疑问?欢迎在评论区留言,让我们一起讨论吧!😊