网站无法处理请求,嵌入式培训机构有哪些,室内装修风格,手机网站模板大全概述
本文的编写初衷#xff0c;是想了解一下Spring Boot2中#xff0c;具体是怎么序列化和反序列化JSR 310日期时间体系的#xff0c;Spring MVC应用场景有如下两个#xff1a;
使用RequestBody来获取JSON参数并封装成实体对象#xff1b;使用ResponseBody来把返回给前…概述
本文的编写初衷是想了解一下Spring Boot2中具体是怎么序列化和反序列化JSR 310日期时间体系的Spring MVC应用场景有如下两个
使用RequestBody来获取JSON参数并封装成实体对象使用ResponseBody来把返回给前端的数据转换成JSON数据。
对于一些Integer、String等基础类型的数据Spring MVC可以通过一些内置转换器来解决无需用户关心但是日期时间类型例如LocalDateTime由于格式多变没有内置转换器可用就需要用户自己来配置和处理了。 阅读本文假设读者初步了解了如何使用Jackson。 测试环境
本文使用Spring Boot2.6.6版本锁定的Jackson版本如下
jackson-bom.version2.13.2.20220328/jackson-bom.versionJackson处理JSR 310日期时间需要引入依赖
dependencygroupIdcom.fasterxml.jackson.datatype/groupIdartifactIdjackson-datatype-jsr310/artifactIdversion2.13.2/version
/dependencySpring Boot自动配置
在spring-boot-autoconfigure包中自动配置了Jackson
package org.springframework.boot.autoconfigure.jackson;Configuration(proxyBeanMethods false)
ConditionalOnClass(ObjectMapper.class)
public class JacksonAutoConfiguration {// 详细代码略
}其中有一段代码配置了ObjectMapper
Bean
Primary
ConditionalOnMissingBean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {return builder.createXmlMapper(false).build();
}可以看到ObjectMapper是由Jackson2ObjectMapperBuilder构建的。
再往下会看到如下代码
Configuration(proxyBeanMethods false)
ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperBuilderConfiguration {BeanScope(prototype)ConditionalOnMissingBeanJackson2ObjectMapperBuilder jacksonObjectMapperBuilder(ApplicationContext applicationContext,ListJackson2ObjectMapperBuilderCustomizer customizers) {Jackson2ObjectMapperBuilder builder new Jackson2ObjectMapperBuilder();builder.applicationContext(applicationContext);customize(builder, customizers);return builder;}private void customize(Jackson2ObjectMapperBuilder builder,ListJackson2ObjectMapperBuilderCustomizer customizers) {for (Jackson2ObjectMapperBuilderCustomizer customizer : customizers) {customizer.customize(builder);}}}发现在这里创建了Jackson2ObjectMapperBuilder并且调用了customize(builder, customizers)方法传入LisJackson2ObjectMapperBuilderCustomizer 进行定制ObjectMapper。
Jackson2ObjectMapperBuilderCustomizer是个接口只有一个方法源码如下
FunctionalInterface
public interface Jackson2ObjectMapperBuilderCustomizer {/*** Customize the JacksonObjectMapperBuilder.* param jacksonObjectMapperBuilder the JacksonObjectMapperBuilder to customize*/void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder);}简单点说Spring Boot会收集容器里面所有的Jackson2ObjectMapperBuilderCustomizer实现类统一对Jackson2ObjectMapperBuilder进行设置从而实现定制ObjectMapper。因此如果我们想个性化定制ObjectMapper只需要实现Jackson2ObjectMapperBuilderCustomizer接口并注册到容器就可以了。
自定义Jackson配置类
废话不多说直接上代码
Component
public class JacksonConfig implements Jackson2ObjectMapperBuilderCustomizer, Ordered {/** 默认日期时间格式 */private final String dateTimeFormat yyyy-MM-dd HH:mm:ss;/** 默认日期格式 */private final String dateFormat yyyy-MM-dd;/** 默认时间格式 */private final String timeFormat HH:mm:ss;Overridepublic void customize(Jackson2ObjectMapperBuilder builder) {// 设置java.util.Date时间类的序列化以及反序列化的格式builder.simpleDateFormat(dateTimeFormat);// JSR 310日期时间处理JavaTimeModule javaTimeModule new JavaTimeModule();DateTimeFormatter dateTimeFormatter DateTimeFormatter.ofPattern(dateTimeFormat);javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));DateTimeFormatter dateFormatter DateTimeFormatter.ofPattern(dateFormat);javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));DateTimeFormatter timeFormatter DateTimeFormatter.ofPattern(timeFormat);javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));builder.modules(javaTimeModule);// 全局转化Long类型为String解决序列化后传入前端Long类型精度丢失问题builder.serializerByType(BigInteger.class, ToStringSerializer.instance);builder.serializerByType(Long.class,ToStringSerializer.instance);}Overridepublic int getOrder() {return 1;}
}这个配置类实现了三种个性化配置
设置java.util.Date时间类的序列化以及反序列化的格式JSR 310日期时间处理全局转化Long类型为String解决序列化后传入前端Long类型缺失精度问题。
当然读者还可以按自己的需求继续进行定制其他配置。
测试
这里用JSR 310日期时间进行测试。
创建实体类User
Data
NoArgsConstructor
AllArgsConstructor
public class User {private Long id;private String name;private LocalDate localDate;private LocalTime localTime;private LocalDateTime localDateTime;
}创建控制器UserController
RestController
RequestMapping(user)
public class UserController {PostMapping(test)public User test(RequestBody User user){System.out.println(user.toString());return user;}}前端传参
{id: 184309536616640512,name: 八卦程序,localDate: 2023-03-01,localTime: 09:35:50,localDateTime: 2023-03-01 09:35:50
}后端返回数据
{id: 184309536616640512,name: 八卦程序,localDate: 2023-03-01,localTime: 09:35:50,localDateTime: 2023-03-01 09:35:50
}可以看到前端传入了什么数据后端就返回了什么数据唯一的区别就是后端返回的id是字符串了可以防止前端例如JavaScript出现精度丢失问题。
同时也证明LocalDateTime等日期时间类型到后端参观了一圈又正常返回了没有被拒也没有遭到后端毒打变形例如变成时间戳回来导致亲妈都不认识了。
前端表白被拒
如果不配置JacksonConfig呢Spring MVC在尝试内置转换器无果后会报异常如下 JSON parse error: Cannot deserialize value of type java.time.LocalDateTime
返回给前端的数据如下
{timestamp: 2023-03-01T09:53:02.15800:00,status: 400,error: Bad Request,path: /user/test
}你懂的被拒了。
总结
核心类ObjectMapper
ObjectMapper是jackson-databind模块最为重要的一个类它完成了数据处理的几乎所有功能。 尽管Spring MVC在处理前端传递的JSON参数时进行了一系列眼花缭乱的操作但是一顿操作猛如虎最终还是靠ObjectMapper来完成序列化和反序列化。因此只需要对Spring Boot默认提供的ObjectMapper进行个性化定制即可。
不要覆盖默认配置
我们通过实现Jackson2ObjectMapperBuilderCustomizer接口并注册到容器进行个性化定制Spring Boot不会覆盖默认ObjectMapper的配置而是进行了合并增强具体还会根据Jackson2ObjectMapperBuilderCustomizer实现类的Order优先级进行排序因此上面的JacksonConfig配置类还实现了Ordered接口。
默认的Jackson2ObjectMapperBuilderCustomizerConfiguration优先级是0因此如果我们想要覆盖配置设置优先级大于0即可。 注意在SpringBoot2环境下不要将自定义的ObjectMapper对象注入容器这样会将原有的ObjectMapper配置覆盖 QueryString格式参数
需要注意的是Jackson不能解决QueryString格式参数的问题因为Spring对于这类参数用的是Converter类型转换机制那就是另一条参数绑定之路了不好意思Jackson没在这条路上帮忙。 需要自定义参数类型转换器来处理日期时间类型需要另写文章介绍了。