1. 程式人生 > 實用技巧 >談談spring-boot-starter-data-redis序列化

談談spring-boot-starter-data-redis序列化

在上一篇中springboot 2.X 整合redis中提到了在spring-boot-starter-data-redis中使用JdkSerializationRedisSerializerl來實現序列化,

這裡看下具體是如何實現的。

1.RedisSerializer介面

在spring-data-redis包下,有一個RedisSerializer介面,提供了序列化和反序列化的基本介面。

public interface RedisSerializer<T> {

	/**
* Serialize the given object to binary data.
*
* @param t object to serialize. Can be {@literal null}.
* @return the equivalent binary data. Can be {@literal null}.
*/
@Nullable
byte[] serialize(@Nullable T t) throws SerializationException; /**
* Deserialize an object from the given binary data.
*
* @param bytes object binary representation. Can be {@literal null}.
* @return the equivalent object instance. Can be {@literal null}.
*/
@Nullable
T deserialize(@Nullable byte[] bytes) throws SerializationException; /**
* Obtain a {@link RedisSerializer} using java serialization.<br />
* <strong>Note:</strong> Ensure that your domain objects are actually {@link java.io.Serializable serializable}.
*
* @return never {@literal null}.
* @since 2.1
*/
static RedisSerializer<Object> java() {
return java(null);
} /**
* Obtain a {@link RedisSerializer} using java serialization with the given {@link ClassLoader}.<br />
* <strong>Note:</strong> Ensure that your domain objects are actually {@link java.io.Serializable serializable}.
*
* @param classLoader the {@link ClassLoader} to use for deserialization. Can be {@literal null}.
* @return new instance of {@link RedisSerializer}. Never {@literal null}.
* @since 2.1
*/
static RedisSerializer<Object> java(@Nullable ClassLoader classLoader) {
return new JdkSerializationRedisSerializer(classLoader);
} /**
* Obtain a {@link RedisSerializer} that can read and write JSON using
* <a href="https://github.com/FasterXML/jackson-core">Jackson</a>.
*
* @return never {@literal null}.
* @since 2.1
*/
static RedisSerializer<Object> json() {
return new GenericJackson2JsonRedisSerializer();
} /**
* Obtain a simple {@link java.lang.String} to {@literal byte[]} (and back) serializer using
* {@link java.nio.charset.StandardCharsets#UTF_8 UTF-8} as the default {@link java.nio.charset.Charset}.
*
* @return never {@literal null}.
* @since 2.1
*/
static RedisSerializer<String> string() {
return StringRedisSerializer.UTF_8;
}
}

可以看到byte[] serialize(@Nullable T t)和T deserialize(@Nullable byte[] bytes)就是序列化和反序列化介面,並且下面還定義了java的JdkSerializationRedisSerializer序列化、json的GenericJackson2JsonRedisSerializer和string的StringRedisSerializer.UTF_8.

2.1 JdkSerializationRedisSerializer序列化

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);
}
}
}

在JdkSerializationRedisSerializer構造方法中,傳入了Converter的兩個物件,serialize的序列化就使用SerializingConverter的convert方法

public byte[] convert(Object source) {
try {
return this.serializer.serializeToByteArray(source);
}
catch (Throwable ex) {
throw new SerializationFailedException("Failed to serialize object using " +
this.serializer.getClass().getSimpleName(), ex);
}
}

Serializer 介面

void serialize(T object, OutputStream outputStream) throws IOException;

default byte[] serializeToByteArray(T object) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
serialize(object, out);
return out.toByteArray();
}

在這裡JdkSerializationRedisSerializer中,使用的是DefaultSerializer,它實現了serialize方法:

public class DefaultSerializer implements Serializer<Object> {

	/**
* Writes the source object to an output stream using Java serialization.
* The source object must implement {@link Serializable}.
* @see ObjectOutputStream#writeObject(Object)
*/
@Override
public void serialize(Object object, OutputStream outputStream) throws IOException {
if (!(object instanceof Serializable)) {
throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " +
"but received an object of type [" + object.getClass().getName() + "]");
}
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(object);
objectOutputStream.flush();
} }

可以看到使用了ObjectOutputStream的writeObject方法來實現的,下面會繼續呼叫writeObject0方法,相關可以檢視ObjectOutputStream的序列化和反序列化

JdkSerializationRedisSerializer的反序列化方式轉化型別有區別,這裡就不詳細介紹了。

2.2 GenericJackson2JsonRedisSerializer序列化

GenericJackson2JsonRedisSerializer主要使用ObjectMapper來實現。

@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);
}
} @Override
public Object deserialize(@Nullable byte[] source) throws SerializationException {
return deserialize(source, Object.class);
}
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);
}
}

檢視writeValueAsBytes方法,並且繼續向下,可以看到使用了jackson相關包進行json化資料。

private final void _serialize(JsonGenerator gen, Object value,
JsonSerializer<Object> ser, PropertyName rootName)
throws IOException
{
try {
gen.writeStartObject();
gen.writeFieldName(rootName.simpleAsEncoded(_config));
ser.serialize(value, gen, this);
gen.writeEndObject();
} catch (Exception e) {
throw _wrapAsIOE(gen, e);
}
}

2.3 StringRedisSerializer

StringRedisTemplate中使用了UTF_8的編碼格式。

public class StringRedisSerializer implements RedisSerializer<String> {

	private final Charset charset;

	/**
* {@link StringRedisSerializer} to use 7 bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode
* character set.
*
* @see StandardCharsets#US_ASCII
* @since 2.1
*/
public static final StringRedisSerializer US_ASCII = new StringRedisSerializer(StandardCharsets.US_ASCII); /**
* {@link StringRedisSerializer} to use ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
*
* @see StandardCharsets#ISO_8859_1
* @since 2.1
*/
public static final StringRedisSerializer ISO_8859_1 = new StringRedisSerializer(StandardCharsets.ISO_8859_1); /**
* {@link StringRedisSerializer} to use 8 bit UCS Transformation Format.
*
* @see StandardCharsets#UTF_8
* @since 2.1
*/
public static final StringRedisSerializer UTF_8 = new StringRedisSerializer(StandardCharsets.UTF_8); /**
* Creates a new {@link StringRedisSerializer} using {@link StandardCharsets#UTF_8 UTF-8}.
*/
public StringRedisSerializer() {
this(StandardCharsets.UTF_8);
} /**
* Creates a new {@link StringRedisSerializer} using the given {@link Charset} to encode and decode strings.
*
* @param charset must not be {@literal null}.
*/
public StringRedisSerializer(Charset charset) { Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
} /*
* (non-Javadoc)
* @see org.springframework.data.redis.serializer.RedisSerializer#deserialize(byte[])
*/
@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));
} @Override
public Class<?> getTargetType() {
return String.class;
}
}

當你的redis資料庫裡面本來存的是字串資料或者你要存取的資料就是字串型別資料的時候,可以使用這種方式,非常簡便。