一步到位 SpringBoot 序列化与消息转换器 (你需要的这里都有)

我亲爱的各位大佬们好
♨️本篇文章记录的为SpringBoot Jackson序列化,ObjectMapper,configureMessageConverters, MappingJackson2HttpMessageConverter消息转换器相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬。
♨️如果文章有什么需要改进的地方还请大佬不吝赐教❤️
个人主页 : 阿千弟

文章目录

    • 序列化与反序列化
      • 1、认识序列化与反序列化
      • 2、为什么要实现对象的序列化和反序列化?
    • Jackson之ObjectMapper对象的使用
      • 1. 引入jackson:
      • 2. 介绍
      • 3.Jackson 注解
        • @JsonProperty
        • @JsonIgnore
        • @JsonIgnoreProperties
        • @JsonIgnoreType
        • @JsonPropertyOrder
        • @JsonRootName
        • @JsonInclude
        • @JsonFormat
      • 4.ObjectMapper常用API
      • 5.配置ObjectMapper
        • 5.1 反序列化时陈列多余字段
        • 5.2 设置输入:禁止把POJO中值为null的字段映射到json字符串中
        • 5.3 long类型精度丢失解决
    • 定制化
      • 数据转化
      • springboot中配置ObjectMapper
      • 添加自定义的消息转换器
      • 添加信息转换器的几种方式:
    • 消息转换器configureMessageConverters和extendMessageConverters区别
      • configureMessageConverters
      • extendMessageConverters
      • 总结

序列化与反序列化

1、认识序列化与反序列化

Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程。

2、为什么要实现对象的序列化和反序列化?

(1)我们创建的Java对象被存储在Java堆中,当程序运行结束后,这些对象会被JVM回收。但在现实的应用中,可能会要求在程序运行结束之后还能读取这些对象,并在以后检索数据,这时就需要用到序列化。

(2)当Java对象通过网络进行传输的时候。因为数据只能够以二进制的形式在网络中进行传输,因此当把对象通过网络发送出去之前需要先序列化成二进制数据,在接收端读到二进制数据之后反序列化成Java对象。

一步到位 SpringBoot 序列化与消息转换器 (你需要的这里都有)_第1张图片

Jackson之ObjectMapper对象的使用

1. 引入jackson:

搭建springMvc框架时,遇到一个Controller接收实体参数问题:method为post,content-type为application/json; charset=utf-8的http请求,Controller接收是@RequestBody ,但无法序列化为Object类型,其它类型却可以,并且报415错误Unsupported Media Type,可以排除注解未开启、请求内容类型不一致问题,那么最有可能是jackson包没引用或者与spring版本号不一致。

Spring5.x引用jackson 2.9版本,Spring4.x引用jackson 2.6,Spring3.x引用jackson 1.9版本都是可以的。

<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <!-- 注意版本应该与 Springboot版本匹配。看SpringBoot版本发布日期去Maven找对应的版本号 -->
</dependency>

这里我用到springboot版本是2.5.4, 所对应的版本jackson版本是2.12.4

2. 介绍

Jackson ObjectMapper类(com.fasterxml.jackson.databind.ObjectMapper)是使用Jackson解析JSON最简单的方法。Jackson ObjectMapper可以从字符串、流或文件解析JSON,并创建Java对象或对象图来表示已解析的JSON。将JSON解析为Java对象也称为从JSON反序列化Java对象
Jackson ObjectMapper也可以从Java对象创建JSON. 从Java对象生成JSON的过程也被称为序列化Java对象到JSON
Jackson对象映射器(Object Mapper)可以把JSON解析为用户自定义类对象, 或者解析为JSON内置的树模型的对象

3.Jackson 注解

@JsonProperty

@JsonProperty 注解指定一个属性用于 JSON 映射,默认情况下映射的 JSON 属性与注解的属性名称相同,不过可以使用该注解的 value 值修改 JSON 属性名,该注解还有一个 index 属性指定生成 JSON 属性的顺序,如果有必要的话。

@JsonIgnore

@JsonIgnore 注解用于排除某个属性,这样该属性就不会被 Jackson 序列化和反序列化。

@JsonIgnoreProperties

@JsonIgnoreProperties 注解是类注解。在序列化为 JSON 的时候,@JsonIgnoreProperties({“prop1”, “prop2”}) 会忽略 pro1 和 pro2 两个属性。在从 JSON 反序列化为 Java 类的时候,@JsonIgnoreProperties(ignoreUnknown=true) 会忽略所有没有 Getter 和 Setter 的属性。该注解在 Java 类和 JSON 不完全匹配的时候很有用。

@JsonIgnoreType

@JsonIgnoreType 也是类注解,会排除所有指定类型的属性。

@JsonPropertyOrder

@JsonPropertyOrder 和 @JsonProperty的 index 属性类似,指定属性序列化时的顺序。

@JsonRootName

@JsonRootName 注解用于指定 JSON 根属性的名称。

@JsonInclude

@JsonInclude(JsonInclude.Include.NON_NULL):对值为 null 的属性不进行序列化
@JsonInclude(JsonInclude.Include.NON_EMPTY) : 表示在序列化时,将值为null的字段排除掉。

@JsonFormat

添加到需要指定格式的日期属性上,指定日期属性序列化与反序列化时的格式。timezone = “GMT+8” 设置时区,表示 +8 小时,否则会少8小时。ObjectMapper 序列化 POJO 对象为 json 字符串时,Date 日期类型默认会转为 long 长整型,json 字符串反序列化为 POJO 时自动将长整型的日期转为 Date 类型。

4.ObjectMapper常用API

Java 对象与 Json 字符串的转换
String writeValueAsString(Object value) 1、用于将任何 Java 对象(如 POJO、List、Set、Map等)序列化为 json 字符串,如果对象中某个属性的值为 null,则默认也会序列化为 null;2、如果 value 为 null,返回序列化的结果也返回 null
byte[] writeValueAsBytes(Object value) 将 java 对象序列化为 字节数组
writeValue(File resultFile, Object value) 将 java 对象序列化并输出指定文件中
writeValue(OutputStream out, Object value) 将 java 对象序列化并输出到指定字节输出流中
writeValue(Writer w, Object value) 将 java 对象序列化并输出到指定字符输出流中
T readValue(String content, Class valueType) 1、从给定的 JSON 字符串反序列化为 Java 对象;2、content 为空或者为 null,都会报错3、valueType 表示反序列化的结果对象,可以是任何 java 对象,比如 POJO、List、Set、Map 等等.
T readValue(byte[] src, Class valueType) 将 json 内容的字节数组反序列化为 java 对象
T readValue(File src, Class valueType) 将本地 json 内容的文件反序列化为 java 对象
T readValue(InputStream src, Class valueType) 将 json 内容的字节输入流反序列化为 java 对象
T readValue(Reader src, Class valueType) 将 json 内容的字符输入流反序列化为 java 对象
T readValue(URL src, Class valueType) 通过网络 url 地址将 json 内容反序列化为 java 对象

5.配置ObjectMapper

ObjectMapper objectMapper = new ObjectMapper();
//去掉默认的时间戳格式     
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
 
//设置为东八区
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
 
// 设置输入:禁止把POJO中值为null的字段映射到json字符串中
objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
 
//空值不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
 
//反序列化时,属性不存在的兼容处理
objectMapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
 
//序列化时,日期的统一格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
 
//序列化日期时以timestamps输出,默认true
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
 
//序列化枚举是以toString()来输出,默认false,即默认以name()来输出
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
 
//序列化枚举是以ordinal()来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);
 
//类为空时,不要抛异常
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
 
//反序列化时,遇到未知属性时是否引起结果失败
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
 
//单引号处理
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
 
//解析器支持解析结束符
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

5.1 反序列化时陈列多余字段

在默认情况下,如果Json数据中有多余的字段,那么在反序列化时Jackson发现无法找到对应的对象字段,便会抛出UnrecognizedPropertyException: Unrecognized field xxx异常,此时可以做如下配置:

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

5.2 设置输入:禁止把POJO中值为null的字段映射到json字符串中

旧的配置方法(在2.9版本后已经过时)

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

新的配置方法

this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

5.3 long类型精度丢失解决

原因 : 前端js对Long类型支持的精度不够,导致后端使用的Long传到前端丢失精度,比如现在分布式id生成算法“雪花算法”在使用中就会出现问题

解决方案:

  1. 在Long类型字段上使用注解标明序列化方式,代码量不大的情况可以考虑@JsonSerialize(using = ToStringSerializer.class)private Long id;

  2. 配置以下代码

SimpleModule simpleModule= newSimpleModule();

simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
//暂时放弃对小long的转换,约定与前端交互数据时,大Long全部转换成字符串
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);

simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);

一步到位 SpringBoot 序列化与消息转换器 (你需要的这里都有)_第2张图片

定制化

当出现特定的需求时,比如:日期的序列化格式。此时需要自定义自己的消息转换器

数据转化

假如有如下场景,前端传递过来一个日期字符串,但是后端需要使用Date类型进行接收,这时就需要一个类型转化器进行转化。

对于时间类型的处理,springmvc给我们提供了一个比较完善的解决方案,使用注解@DateTimeFormat,同时配合jackson提供的@JsonFormat注解几乎可以满足我们的所有需求。

@DateTimeFormat:当从requestParam中获取string参数并需要转化为Date类型时,会根据此注解的参数pattern的格式进行转化。

@JsonFormat:当从请求体中获取json字符序列,需要反序列化为对象时,时间类型会按照这个注解的属性内容进行处理。

这两个注解需要加在实体类的对应字段上即可:

// 对象和json互相转化的过程当中按照此转化方式转哈
@JsonFormat(
            pattern = "yyyy年MM月dd日",
            timezone = "GMT-8"
    )
private Date birthday;
// 从requestParam中获取参数并且转化
@DateTimeFormat(pattern = "yyyy年MM月dd日")
private Date birthday;

处理的过程大致如下:

一步到位 SpringBoot 序列化与消息转换器 (你需要的这里都有)_第3张图片

springboot中配置ObjectMapper

@Configuration
public class CustomObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public CustomObjectMapper() {
        super();
        //去掉默认的时间戳格式
        this.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //设置为东八区
        this.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 设置输入:禁止把POJO中值为null的字段映射到json字符串中
        //this.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
        this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        // 空值不序列化
        this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 序列化枚举是以toString()来输出,默认false,即默认以name()来输出
        this.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);

        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

添加自定义的消息转换器

我的项目中mvc配置是直接实现的WebMvcConfigurer,这两里面的方法一样,不同的是WebMvcConfigurationSupport里面有很多springboot为我们做的配置,而WebMvcConfigurer里面是空的;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

//    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    
    // 这个方法是用来配置静态资源的,比如html,js,css,等等
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry){       
    }
       
    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {    
    }

    //拓展mvc框架的消息转换器
    @Autowired
    private CustomObjectMapper customObjectMapper;
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置消息转换器,底层使用jackson将Java对象转成json
        messageConverter.setObjectMapper(customObjectMapper);
        //将我们自定义的消息转换器追加到mvc框架的集合转换器中
        converters.add(0, messageConverter);
    }
}

添加信息转换器的几种方式:

1.注解Bean形式:

// 这样做springboot会把我们自定义的converter放在顺序上的最高优先级(List的头部)
// 即有多个converter都满足Accpet/ContentType/MediaType的规则时,优先使用我们这个
@Bean
public MappingJackson2HttpMessageConverter  mappingJackson2HttpMessageConverter(){
		return new MappingJackson2HttpMessageConverter();
}

2.springboot通过继承WebMvcConfigurerAdapter,重写configureMessageConverters。
(Spring3.x 用MappingJacksonHttpMessageConverter
Spring4.x 用MappingJackson2HttpMessageConverter)

// 通常在只有一个自定义WebMvcConfigurerAdapter时,会把这个方法里面添加的converter(s)依次放在最高优先级(List的头部)
// 虽然第一种方式的代码先执行,但是bean的添加比这种方式晚,所以方式二的优先级 大于 方式一
@Configuration
@EnableWebMvc
public class WebMvcConfigure extends WebMvcConfigurerAdapter {

	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
		MappingJackson2HttpMessageConverter mjhmc=new MappingJackson2HttpMessageConverter(objectMapper());
		converters.add(mjhmc);
	}

	@Bean
	public ObjectMapper objectMapper(){
		ObjectMapper om= Jackson2ObjectMapperBuilder.json().build();
		om.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
		om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		SimpleModule simpleModule = new SimpleModule();
		simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
		simpleModule.addSerializer(Long.TYPE,ToStringSerializer.instance);
		simpleModule.addSerializer(SwordsAbstractModel.class,new CustomerJpaModelSerializer());
		om.registerModule(simpleModule);
		return om;
	}
}

3.extendMessageConverters方式:

// 添加converter的第三种方式
// 同一个WebMvcConfigurerAdapter中的configureMessageConverters方法先于extendMessageConverters方法执行
// 可以理解为是三种方式中最后执行的一种,不过这里可以通过add指定顺序来调整优先级,也可以使用remove/clear来删除converter,功能强大
// 使用converters.add(xxx)会放在最低优先级(List的尾部)
// 使用converters.add(0,xxx)会放在最高优先级(List的头部)
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		converters.add(new MappingJackson2HttpMessageConverter());
}

消息转换器configureMessageConverters和extendMessageConverters区别

在spring中配置WebMvc时有两种方法,一种是继承WebMvcConfigurationSupport,重写里面相应的方法,还有一种是继承WebMvcConfigurer的子抽象类WebMvcConfigurerAdapter,也是重写里面相应的方法,但是需要在配置类上添加@EnableWebMvc注解。那这两个类直接是什么关系呢?

configureMessageConverters

配置HttpMessageConverters以用于读取或写入请求或响应的主体。如果未添加转换器,则会注册转换器的默认列表。请注意,向列表中添加转换器将关闭默认转换器注册。只需添加一个转换器而不影响默认注册,请考虑使用extendMessageConverters

extendMessageConverters

用于在配置转换器后扩展或修改转换器列表的钩子。这可能很有用,例如允许注册默认转换器,然后通过此方法插入自定义转换器。

总结

添加自定义消息转换器不覆盖默认转换器请使用extendMessageConverters
添加自定义会覆盖默认转换器使用configureMessageConverters

一步到位 SpringBoot 序列化与消息转换器 (你需要的这里都有)_第4张图片

如果这篇【文章】有帮助到你,希望可以给我点个赞,创作不易,如果有对Java后端或者对spring感兴趣的朋友,请多多关注
个人主页 : 阿千弟

你可能感兴趣的:(java,spring,boot,spring)