1.提供的几种序列化方式
2.详细对比(只看spring封装的,fastjson提供的序列化不考虑使用)
2.1 ByteArrayRedisSerializer
2.1.1 序列化源码
通过枚举实现的单例,仅可存储byte[]
java
enum ByteArrayRedisSerializer implements RedisSerializer<byte[]> {
INSTANCE;
@Nullable
@Override
public byte[] serialize(@Nullable byte[] bytes) throws SerializationException {
return bytes;
}
@Nullable
@Override
public byte[] deserialize(@Nullable byte[] bytes) throws SerializationException {
return bytes;
}
}
2.1.2 测试详情
java
@Bean
public RedisTemplate<String, Object> objectRedisTemplate(@Qualifier(value = "redisConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String, Object>
redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(RedisSerializer.byteArray());
return redisTemplate;
}
@Test
public void test() {
byte[] bytes = new byte[1];
bytes[0] = 1;
ValueOperations<String, byte[]> stringObjectValueOperations = objectRedisTemplate.opsForValue();
stringObjectValueOperations.set("test", bytes);
byte[] object = stringObjectValueOperations.get("test");
System.out.println(object[0]);//成功输出1 }
}
2.1.3 小结
- 仅支持byte[],其他数据类型会报转换异常
- 存入的数据可读性差
2.2 GenericJackson2JsonRedisSerializer
2.2.1 序列化源码
通用Jackson2转换,通过在序列化的值中设置类路径信息,达到通用转换的结果
java
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
java
private final ObjectMapper mapper;
@Override
public byte[] serialize(@Nullable Object source) throws SerializationException {
if (source == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return mapper.writeValueAsBytes(source);
} catch (JsonProcessingException e) {
throw new SerializationException("Could not write JSON: " + e.getMessage(), e);
}
}
@Nullable
public <T> T deserialize(@Nullable byte[] source, Class<T> type) throws SerializationException {
Assert.notNull(type,
"Deserialization type must not be null! Please provide Object.class to make use of Jackson2 default typing.");
if (SerializationUtils.isEmpty(source)) {
return null;
}
try {
return mapper.readValue(source, type);
} catch (Exception ex) {
throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
}
}
2.2.2 测试详情
java
@Bean
public RedisTemplate<String, Object> objectRedisTemplate(@Qualifier(value = "redisConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
@Test
public void test() {
RedisObj redisObj = new RedisObj();
redisObj.setName("张三");
redisObj.setId(1);
ValueOperations<String, RedisObj> stringObjectValueOperations = objectRedisTemplate.opsForValue();
stringObjectValueOperations.set("test", redisObj);
RedisObj res = stringObjectValueOperations.get("test");
System.out.println(res);
}
存入的为json数据,并记录了存入对象的路径
当删除对象中某个成员变量,会导致deserialize失败
解决方案:自定义ObjectMapper
java
@Bean
public RedisTemplate<String, Object> objectRedisTemplateOne(@Qualifier(value = "redisConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
ObjectMapper objectMapper = new ObjectMapper();
//遇到未知属性不处理
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//在json中写入class信息,用于反序列化
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));
return redisTemplate;
}
2.2.3 小结
- 不能随意修改类名称,路径,以及删除对象中的成员变量,会导致序列化失败
- 存入的数据为json,可读性高
2.3 GenericToStringSerializer
2.3.1 序列化源码
使用Converter将对象转换成string
java
@Override
public T deserialize(@Nullable byte[] bytes) {
if (bytes == null) {
return null;
}
String string = new String(bytes, charset);
return converter.convert(string, type);
}
@Override
public byte[] serialize(@Nullable T object) {
if (object == null) {
return null;
}
String string = converter.convert(object, String.class);
return string.getBytes(charset);
}
支持转换的类型
java
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));
converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharsetConverter());
converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCurrencyConverter());
converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}
public static void addCollectionConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
converterRegistry.addConverter(new MapToMapConverter(conversionService));
converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
converterRegistry.addConverter(new StringToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
converterRegistry.addConverter(new StreamConverter(conversionService));
}
2.3.2 小结
- 不同的对象需要写不同的转换代码,复杂度高,不建议使用
2.4Jackson2JsonRedisSerializer
2.4.1 序列化源码
可以使用Jackson和Jackson的Databind ObjectMapper读取和写入JSON。此转换器可用于绑定到类型化的bean或非类型化的HashMap实例
java
public class Jackson2JsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
//需要转换的Class对象
private final JavaType javaType;
private ObjectMapper objectMapper = new ObjectMapper();
public Jackson2JsonRedisSerializer(Class<T> type) {
this.javaType = getJavaType(type);
}
public Jackson2JsonRedisSerializer(JavaType javaType) {
this.javaType = javaType;
}
@SuppressWarnings("unchecked")
public T deserialize(@Nullable byte[] bytes) throws SerializationException {
if (SerializationUtils.isEmpty(bytes)) {
return null;
}
try {
return (T) this.objectMapper.readValue(bytes, 0, bytes.length, javaType);
} catch (Exception ex) {
throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
}
}
@Override
public byte[] serialize(@Nullable Object t) throws SerializationException {
if (t == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return this.objectMapper.writeValueAsBytes(t);
} catch (Exception ex) {
throw new SerializationException("Could not write JSON: " + ex.getMessage(), ex);
}
}
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}
2.4.2 测试详情
测试代码
java
@Bean
public RedisTemplate<String, Object> objectRedisTemplateOne(@Qualifier(value = "redisConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(RedisObj.class));
return redisTemplate;
}
@Test
public void test() {
RedisConfig.RedisObj redisObj = new RedisConfig.RedisObj();
redisObj.setName("张三");
redisObj.setId(2);
ValueOperations<String, RedisConfig.RedisObj> stringObjectValueOperations = objectRedisTemplate.opsForValue();
stringObjectValueOperations.set("test", redisObj);
RedisConfig.RedisObj res = stringObjectValueOperations.get("test");
System.out.println(res);
}
存入的为json数据,没有存入对象的路径
当删除对象中某个成员变量,同样会导致deserialize失败
解决方案:自定义ObjectMapper
java
@Bean
public RedisTemplate<String, Object> objectRedisTemplateOne(@Qualifier(value = "redisConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer<RedisObj> redisObjJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(RedisObj.class);
ObjectMapper objectMapper = new ObjectMapper();
//遇到未知属性不处理
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
redisObjJackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(redisObjJackson2JsonRedisSerializer);
return redisTemplate;
}
2.4.3 小结
- 需要在@Bean注入的时候设置序列化对象,通用性较差
- 和GenericJackson2JsonRedisSerializer的区别
java
//GenericJackson2JsonRedisSerializer在源码中对ObjectMapper设置了通用类型处理,不需要在单独设置序列化的对象,需要考虑修改包名,路径导致的序列化失败问题
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
// 而Jackson2JsonRedisSerializer需要在设置序列化方式的时候设置具体的对象,在实际开发中这是难以接受的
new Jackson2JsonRedisSerializer<>(RedisObj.class);
2.5 JdkSerializationRedisSerializer
2.5.1 序列化源码
java
public class JdkSerializationRedisSerializer implements RedisSerializer<Object> {
private final Converter<Object, byte[]> serializer;
private final Converter<byte[], Object> deserializer;
/**
* Creates a new {@link JdkSerializationRedisSerializer} using the default class loader.
*/
public JdkSerializationRedisSerializer() {
this(new SerializingConverter(), new DeserializingConverter());
}
/**
* Creates a new {@link JdkSerializationRedisSerializer} using a {@link ClassLoader}.
*
* @param classLoader the {@link ClassLoader} to use for deserialization. Can be {@literal null}.
* @since 1.7
*/
public JdkSerializationRedisSerializer(@Nullable ClassLoader classLoader) {
this(new SerializingConverter(), new DeserializingConverter(classLoader));
}
/**
* Creates a new {@link JdkSerializationRedisSerializer} using a {@link Converter converters} to serialize and
* deserialize objects.
*
* @param serializer must not be {@literal null}
* @param deserializer must not be {@literal null}
* @since 1.7
*/
public JdkSerializationRedisSerializer(Converter<Object, byte[]> serializer, Converter<byte[], Object> deserializer) {
Assert.notNull(serializer, "Serializer must not be null!");
Assert.notNull(deserializer, "Deserializer must not be null!");
this.serializer = serializer;
this.deserializer = deserializer;
}
public Object deserialize(@Nullable byte[] bytes) {
if (SerializationUtils.isEmpty(bytes)) {
return null;
}
try {
return deserializer.convert(bytes);
} catch (Exception ex) {
throw new SerializationException("Cannot deserialize", ex);
}
}
@Override
public byte[] serialize(@Nullable Object object) {
if (object == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return serializer.convert(object);
} catch (Exception ex) {
throw new SerializationException("Cannot serialize", ex);
}
}
}
2.5.2 测试详情
java
@Bean
public RedisTemplate<String, Object> objectRedisTemplateOne(@Qualifier(value = "redisConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
@Test
public void test() {
RedisConfig.RedisObj redisObj = new RedisConfig.RedisObj();
redisObj.setName("张三");
redisObj.setId(2);
ValueOperations<String, RedisConfig.RedisObj> stringObjectValueOperations = objectRedisTemplate.opsForValue();
stringObjectValueOperations.set("test", redisObj);
RedisConfig.RedisObj res = stringObjectValueOperations.get("test");
System.out.println(res);
}
存入的为JDK序列化后的数据,可读性较差
修改对象,会导致序列化失败
2.5.3 小结
- 修改对象后,无法序列化
- 序列化后的数据可读性差
- 需要占用较大的存储空间
2.6 StringRedisSerializer
2.6.1 序列化源码
提供简单String到byte[]的序列化程序
java
@Override
public String deserialize(@Nullable byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.serializer.RedisSerializer#serialize(java.lang.Object)
*/
@Override
public byte[] serialize(@Nullable String string) {
return (string == null ? null : string.getBytes(charset));
}
2.6.2 测试详情
java
/**
spring 已经提供对于操作string类型的StringRedisTemplate,直接使用即可
**/
@Bean
public StringRedisTemplate objectRedisTemplateOne(@Qualifier(value = "redisConnectionFactory") RedisConnectionFactory factory) {
StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
@Test
public void test() {
RedisConfig.RedisObj redisObj = new RedisConfig.RedisObj();
redisObj.setName("张三");
redisObj.setId(2);
ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
stringValueOperations.set("test", JSON.toJSONString(redisObj));
String res = stringValueOperations.get("test");
System.out.println(res);
}
2.6.3 小结
- 存入字符串,扩展性较强,可读性较强
3. 总结
- 在实际开发中,建议使用StringRedisTemplate进行redis操作,拿到string后在业务中进行对应的转换
- 不想在业务中进行转换可以使用GenericToStringSerializer进行序列化操作,但是需要注意class名,路径和成员变量的修改导致的序列化失败问题
- ByteArrayRedisSerializer,GenericToStringSerializer,JdkSerializationRedisSerializer 不建议使用