Springboot 开发 -- 序列化与消息转换器

一、序列化与反序列化

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

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

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

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

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

二、Spring Boot中的序列化技术

Spring Boot 主要使用Jackson库来处理JSON的序列化和反序列化

1.引入 Jackson库 配置

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

当我们使用 spring-boot-starter-web 时,默认引入 jackson 库

2. Jackson ObjectMapper类配置

  1. ObjectMapper是Jackson中用于数据绑定的核心类。通过自定义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);
  1. 反序列化时陈列多余字段
    在默认情况下,如果Json数据中有多余的字段,那么在反序列化时Jackson发现无法找到对应的对象字段,便会抛出UnrecognizedPropertyException: Unrecognized field xxx异常,此时可以做如下配置:
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

3. 自定义序列化器和反序列化器

对于特殊的数据类型,可能需要自定义序列化器和反序列化器。通过实现JsonSerializer和JsonDeserializer接口,可以控制特定类型对象的序列化和反序列化过程。

配置 pojo 中 null 值转 空字符串示例:

  • 首先,你需要一个自定义的序列化器,用于处理 null 值的情况。以下是一个简单的例子,它使用了 JsonSerializer 来检查字段值,如果为 null,则写入空字符串:
import com.fasterxml.jackson.core.JsonGenerator;  
import com.fasterxml.jackson.databind.JsonSerializer;  
import com.fasterxml.jackson.databind.SerializerProvider;  
import java.io.IOException;  
  
public class NullToEmptyStringSerializer extends JsonSerializer<Object> {  
  
    @Override  
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {  
        if (value == null) {  
            gen.writeString("");  
        } else {  
            // 默认序列化器处理非null值  
            serializers.defaultSerializeValue(value, gen);  
        }  
    }  
}
  • 上面的序列化器对于所有字段都使用相同的逻辑,这可能不是你想要的。你可能只想对某些字段进行此处理。为了实现这一点,你可以使用 @JsonSerialize 注解并指定你的自定义序列化器,如下所示:
import com.fasterxml.jackson.databind.annotation.JsonSerialize;  
  
public class MyPojo {  
  
    private String field1;  
  
    @JsonSerialize(using = NullToEmptyStringSerializer.class)  
    private String field2; // 只有这个字段的null值会被转换为空字符串  
  
    // getters and setters  
}

4. 配置消息转换器的方式

  1. 注解 Bean 形式自定义消息转换器:
    可以通过实现Spring MVC中的HttpMessageConverter接口来创建自定义的消息转换器。但更常见的是,扩展或配置现有的消息转换器,如MappingJackson2HttpMessageConverter。
@Configuration  
public class WebConfig {  

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



  1. springboot通过继承WebMvcConfigurerAdapter,重写configureMessageConverters。
// 通常在只有一个自定义WebMvcConfigurerAdapter时,会把这个方法里面添加的converter(s)依次放在最高优先级(List的头部)
// 虽然第一种方式的代码先执行,但是bean的添加比这种方式晚,所以方式二的优先级 大于 方式一
@Configuration
@EnableWebMvc
public class WebMvcConfigure extends WebMvcConfigurerAdapter {
   
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
   
        MappingJackson2HttpMessageConverter convert = new MappingJackson2HttpMessageConverter(objectMapper());
        converters.add(convert);
    }

    @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;
    }
}
  1. extendMessageConverters方式:

想要在不替换默认转换器的情况下添加或修改转换器,可以使用WebMvcConfigurer的extendMessageConverters方法。

@Configuration  
public class WebConfig implements WebMvcConfigurer {  
 
    @Override  
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {  
        for (int i = 0; i < converters.size(); i++) {  
            // 检查或修改现有的转换器  
            // 例如:如果找到MappingJackson2HttpMessageConverter,可以对其进行配置  
        }  
          
        // 也可以添加新的转换器  
        converters.add(new CustomHttpMessageConverter()); 
        converters.add(new MappingJackson2HttpMessageConverter()); 
    }  
}

5. 配置Jackson

如果主要关心的是JSON序列化和反序列化,可能不需要自定义整个HttpMessageConverter。相反,你可以配置默认的MappingJackson2HttpMessageConverter所使用的Jackson库。这可以通过在application.properties或application.yml中添加相关属性,或者通过编程方式配置ObjectMapper来实现。 例如,在application.properties中配置Jackson的日期格式:

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss  
spring.jackson.time-zone=Asia/Shanghai

或者通过配置一个Jackson2ObjectMapperBuilderCustomizer bean来定制ObjectMapper:

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
        // 其他配置...
        return mapper;
    }

    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper);
        return converter;
    }
}

三、 消息转换器 配置 Redis 序列化 实例

在 Redis 中,数据通常以字节(byte)的形式存储,因此当我们使用 Redis 客户端(如 Jedis、Lettuce 或 RedisTemplate 在 Spring Data Redis 中)时,我们需要配置序列化器(serializers)来将 Java 对象转换为字节,并在需要时从字节转换回 Java 对象。

在 Spring Data Redis 中,RedisTemplate 是与 Redis 进行交互的核心组件。为了配置消息转换器或序列化器,通常会设置 RedisTemplate 的 keySerializer、valueSerializer、hashKeySerializer 和 hashValueSerializer。

以下是一个配置 RedisTemplate 以使用 JSON 序列化的示例,这里我们使用 Jackson2JsonRedisSerializer 作为序列化器:

import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.data.redis.connection.RedisConnectionFactory;  
import org.springframework.data.redis.core.RedisTemplate;  
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;  
import org.springframework.data.redis.serializer.StringRedisSerializer;  
  
import com.fasterxml.jackson.annotation.JsonAutoDetect;  
import com.fasterxml.jackson.annotation.PropertyAccessor;  
import com.fasterxml.jackson.databind.ObjectMapper;  
  
@Configuration  
public class RedisConfig {  
  
    @Bean
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		// 配置连接工厂
		template.setConnectionFactory(factory);

		//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
		Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

		ObjectMapper om = new ObjectMapper();
		// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
		om.activateDefaultTyping( LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
		// 空字段不序列化
		om.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
		// 解决jackson2无法反序列化LocalDateTime的问题
		om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
		om.registerModule(new JavaTimeModule());

		jsonRedisSerializer.setObjectMapper(om);

		// 值采用json序列化
		template.setValueSerializer(jsonRedisSerializer);
		//使用StringRedisSerializer来序列化和反序列化redis的key值
		template.setKeySerializer(new StringRedisSerializer());

		//设置hash key 和value序列化模式
		template.setHashKeySerializer(new StringRedisSerializer());
		template.setHashValueSerializer(jsonRedisSerializer);
		template.afterPropertiesSet();

		return template;
	}
}

在这个配置中,我们为 key 使用了 StringRedisSerializer,因为它可以很好地处理字符串。对于 value 和 hash 的 key/value,我们使用了 Jackson2JsonRedisSerializer,它使用 Jackson 库将 Java 对象转换为 JSON 格式的字符串,并可以反向操作。

参考:
https://developer.aliyun.com/article/1233496

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值