一、序列化与反序列化
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类配置
- 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);
- 反序列化时陈列多余字段
在默认情况下,如果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. 配置消息转换器的方式
- 注解 Bean 形式自定义消息转换器:
可以通过实现Spring MVC中的HttpMessageConverter接口来创建自定义的消息转换器。但更常见的是,扩展或配置现有的消息转换器,如MappingJackson2HttpMessageConverter。
@Configuration
public class WebConfig { // 这样做springboot会把我们自定义的converter放在顺序上的最高优先级(List的头部)// 即有多个converter都满足Accpet/ContentType/MediaType的规则时,优先使用我们这个@Beanpublic MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){return new MappingJackson2HttpMessageConverter();}
}
- springboot通过继承WebMvcConfigurerAdapter,重写configureMessageConverters。
// 通常在只有一个自定义WebMvcConfigurerAdapter时,会把这个方法里面添加的converter(s)依次放在最高优先级(List的头部)
// 虽然第一种方式的代码先执行,但是bean的添加比这种方式晚,所以方式二的优先级 大于 方式一
@Configuration
@EnableWebMvc
public class WebMvcConfigure extends WebMvcConfigurerAdapter {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters){MappingJackson2HttpMessageConverter convert = new MappingJackson2HttpMessageConverter(objectMapper());converters.add(convert);}@Beanpublic 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;}
}
- 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 {@Beanpublic 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;}@Beanpublic 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 { @Beanpublic 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和publicom.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