Refine RedisSerializer implementations.
This commit polishes up method ordering, introduces Javadoc where missing and updates nullability annotations and argument names. Closes #1097
This commit is contained in:
@@ -29,8 +29,8 @@ enum ByteArrayRedisSerializer implements RedisSerializer<byte[]> {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public byte[] serialize(@Nullable byte[] bytes) throws SerializationException {
|
||||
return bytes;
|
||||
public byte[] serialize(@Nullable byte[] value) throws SerializationException {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -31,7 +31,7 @@ class DefaultRedisElementReader<T> implements RedisElementReader<T> {
|
||||
|
||||
private final @Nullable RedisSerializer<T> serializer;
|
||||
|
||||
DefaultRedisElementReader(RedisSerializer<T> serializer) {
|
||||
DefaultRedisElementReader(@Nullable RedisSerializer<T> serializer) {
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class DefaultRedisElementWriter<T> implements RedisElementWriter<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer write(T value) {
|
||||
public ByteBuffer write(@Nullable T value) {
|
||||
|
||||
if (serializer != null && (value == null || serializer.canSerialize(value.getClass()))) {
|
||||
return ByteBuffer.wrap(serializer.serialize(value));
|
||||
@@ -51,6 +51,5 @@ class DefaultRedisElementWriter<T> implements RedisElementWriter<T> {
|
||||
|
||||
throw new IllegalStateException(
|
||||
String.format("Cannot serialize value of type %s without a serializer", value.getClass()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,21 +66,6 @@ import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
*/
|
||||
public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Object> {
|
||||
|
||||
/**
|
||||
* Register {@link NullValueSerializer} in the given {@link ObjectMapper} with an optional
|
||||
* {@code classPropertyTypeName}. This method should be called by code that customizes
|
||||
* {@link GenericJackson2JsonRedisSerializer} by providing an external {@link ObjectMapper}.
|
||||
*
|
||||
* @param objectMapper the object mapper to customize.
|
||||
* @param classPropertyTypeName name of the type property. Defaults to {@code @class} if {@literal null}/empty.
|
||||
* @since 2.2
|
||||
*/
|
||||
public static void registerNullValueSerializer(ObjectMapper objectMapper, @Nullable String classPropertyTypeName) {
|
||||
|
||||
// Simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here
|
||||
// since we need the type hint embedded for deserialization using the default typing feature.
|
||||
objectMapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(classPropertyTypeName)));
|
||||
}
|
||||
|
||||
private final JacksonObjectReader reader;
|
||||
|
||||
@@ -203,6 +188,22 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
|
||||
.or("@class");
|
||||
}
|
||||
|
||||
/**
|
||||
* Register {@link NullValueSerializer} in the given {@link ObjectMapper} with an optional
|
||||
* {@code classPropertyTypeName}. This method should be called by code that customizes
|
||||
* {@link GenericJackson2JsonRedisSerializer} by providing an external {@link ObjectMapper}.
|
||||
*
|
||||
* @param objectMapper the object mapper to customize.
|
||||
* @param classPropertyTypeName name of the type property. Defaults to {@code @class} if {@literal null}/empty.
|
||||
* @since 2.2
|
||||
*/
|
||||
public static void registerNullValueSerializer(ObjectMapper objectMapper, @Nullable String classPropertyTypeName) {
|
||||
|
||||
// Simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here
|
||||
// since we need the type hint embedded for deserialization using the default typing feature.
|
||||
objectMapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(classPropertyTypeName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured {@link ObjectMapper} used internally by this {@link GenericJackson2JsonRedisSerializer}
|
||||
* to de/serialize {@link Object objects} as {@literal JSON}.
|
||||
@@ -214,14 +215,14 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable Object source) throws SerializationException {
|
||||
public byte[] serialize(@Nullable Object value) throws SerializationException {
|
||||
|
||||
if (source == null) {
|
||||
if (value == null) {
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
try {
|
||||
return writer.write(mapper, source);
|
||||
return writer.write(mapper, value);
|
||||
} catch (IOException cause) {
|
||||
String message = String.format("Could not write JSON: %s", cause.getMessage());
|
||||
throw new SerializationException(message, cause);
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -42,8 +41,7 @@ public class GenericToStringSerializer<T> implements RedisSerializer<T>, BeanFac
|
||||
|
||||
private final Class<T> type;
|
||||
private final Charset charset;
|
||||
|
||||
private Converter converter = new Converter(new DefaultConversionService());
|
||||
private Converter converter;
|
||||
|
||||
public GenericToStringSerializer(Class<T> type) {
|
||||
this(type, StandardCharsets.UTF_8);
|
||||
@@ -55,20 +53,44 @@ public class GenericToStringSerializer<T> implements RedisSerializer<T>, BeanFac
|
||||
|
||||
this.type = type;
|
||||
this.charset = charset;
|
||||
this.converter = new Converter(DefaultConversionService.getSharedInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ConversionService} to be used.
|
||||
*
|
||||
* @param conversionService the conversion service to be used, must not be {@literal null}.
|
||||
*/
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
|
||||
Assert.notNull(conversionService, "non null conversion service required");
|
||||
Assert.notNull(conversionService, "ConversionService must not be null");
|
||||
|
||||
converter = new Converter(conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link TypeConverter} to be used.
|
||||
*
|
||||
* @param typeConverter the conversion service to be used, must not be {@literal null}.
|
||||
*/
|
||||
public void setTypeConverter(TypeConverter typeConverter) {
|
||||
|
||||
Assert.notNull(typeConverter, "non null type converter required");
|
||||
Assert.notNull(typeConverter, "TypeConverter must not be null");
|
||||
|
||||
converter = new Converter(typeConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable T value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String string = converter.convert(value, String.class);
|
||||
return string.getBytes(charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deserialize(@Nullable byte[] bytes) {
|
||||
|
||||
@@ -81,29 +103,14 @@ public class GenericToStringSerializer<T> implements RedisSerializer<T>, BeanFac
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable T object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
String string = converter.convert(object, String.class);
|
||||
return string.getBytes(charset);
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
|
||||
// TODO: This code can never happen...
|
||||
if (converter == null && beanFactory instanceof ConfigurableBeanFactory) {
|
||||
ConfigurableBeanFactory cFB = (ConfigurableBeanFactory) beanFactory;
|
||||
ConversionService conversionService = cFB.getConversionService();
|
||||
|
||||
converter = (conversionService != null ? new Converter(conversionService)
|
||||
: new Converter(cFB.getTypeConverter()));
|
||||
}
|
||||
// no-op
|
||||
}
|
||||
|
||||
private class Converter {
|
||||
private final ConversionService conversionService;
|
||||
private final TypeConverter typeConverter;
|
||||
private final static class Converter {
|
||||
|
||||
private final @Nullable ConversionService conversionService;
|
||||
private final @Nullable TypeConverter typeConverter;
|
||||
|
||||
public Converter(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
@@ -115,11 +122,11 @@ public class GenericToStringSerializer<T> implements RedisSerializer<T>, BeanFac
|
||||
this.typeConverter = typeConverter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
<E> E convert(Object value, Class<E> targetType) {
|
||||
if (conversionService != null) {
|
||||
return conversionService.convert(value, targetType);
|
||||
}
|
||||
return typeConverter.convertIfNecessary(value, targetType);
|
||||
|
||||
return conversionService != null ? conversionService.convert(value, targetType)
|
||||
: typeConverter.convertIfNecessary(value, targetType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,32 +126,6 @@ public class Jackson2JsonRedisSerializer<T> implements RedisSerializer<T> {
|
||||
this.javaType = javaType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T deserialize(@Nullable byte[] bytes) throws SerializationException {
|
||||
|
||||
if (SerializationUtils.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (T) this.reader.read(this.mapper, bytes, 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.writer.write(this.mapper, t);
|
||||
} catch (Exception ex) {
|
||||
throw new SerializationException("Could not write JSON: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code ObjectMapper} for this view. If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper}
|
||||
* is used.
|
||||
@@ -171,6 +145,33 @@ public class Jackson2JsonRedisSerializer<T> implements RedisSerializer<T> {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable T value) throws SerializationException {
|
||||
|
||||
if (value == null) {
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
try {
|
||||
return this.writer.write(this.mapper, value);
|
||||
} catch (Exception ex) {
|
||||
throw new SerializationException("Could not write JSON: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T deserialize(@Nullable byte[] bytes) throws SerializationException {
|
||||
|
||||
if (SerializationUtils.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (T) this.reader.read(this.mapper, bytes, javaType);
|
||||
} catch (Exception ex) {
|
||||
throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Jackson {@link JavaType} for the specific class.
|
||||
* <p>
|
||||
|
||||
@@ -81,6 +81,20 @@ public class JdkSerializationRedisSerializer implements RedisSerializer<Object>
|
||||
this.deserializer = RedisAssertions.requireNonNull(deserializer, "Deserializer must not be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable Object value) {
|
||||
|
||||
if (value == null) {
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
try {
|
||||
return serializer.convert(value);
|
||||
} catch (Exception cause) {
|
||||
throw new SerializationException("Cannot serialize", cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(@Nullable byte[] bytes) {
|
||||
|
||||
@@ -94,18 +108,4 @@ public class JdkSerializationRedisSerializer implements RedisSerializer<Object>
|
||||
throw new SerializationException("Cannot deserialize", cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable Object object) {
|
||||
|
||||
if (object == null) {
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
try {
|
||||
return serializer.convert(object);
|
||||
} catch (Exception cause) {
|
||||
throw new SerializationException("Cannot serialize", cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,24 @@ public class OxmSerializer implements InitializingBean, RedisSerializer<Object>
|
||||
Assert.state(unmarshaller != null, "non-null unmarshaller required");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable Object value) throws SerializationException {
|
||||
|
||||
if (value == null) {
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
StreamResult result = new StreamResult(stream);
|
||||
|
||||
try {
|
||||
marshaller.marshal(value, result);
|
||||
} catch (Exception ex) {
|
||||
throw new SerializationException("Cannot serialize object", ex);
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(@Nullable byte[] bytes) throws SerializationException {
|
||||
|
||||
@@ -97,22 +115,4 @@ public class OxmSerializer implements InitializingBean, RedisSerializer<Object>
|
||||
throw new SerializationException("Cannot deserialize bytes", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable Object t) throws SerializationException {
|
||||
|
||||
if (t == null) {
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
StreamResult result = new StreamResult(stream);
|
||||
|
||||
try {
|
||||
marshaller.marshal(t, result);
|
||||
} catch (Exception ex) {
|
||||
throw new SerializationException("Cannot serialize object", ex);
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.redis.serializer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -36,7 +37,7 @@ public interface RedisElementWriter<T> {
|
||||
* @param element can be {@literal null}.
|
||||
* @return the {@link ByteBuffer} representing {@code element} in its binary form.
|
||||
*/
|
||||
ByteBuffer write(T element);
|
||||
ByteBuffer write(@Nullable T element);
|
||||
|
||||
/**
|
||||
* Create new {@link RedisElementWriter} using given {@link RedisSerializer}.
|
||||
|
||||
@@ -20,8 +20,9 @@ import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Basic interface serialization and deserialization of Objects to byte arrays (binary data). It is recommended that
|
||||
* implementations are designed to handle null objects/empty arrays on serialization and deserialization side. Note that
|
||||
* Redis does not accept null keys or values but can return null replies (for non existing keys).
|
||||
* implementations are designed to handle {@literal null} objects/empty arrays on serialization and deserialization
|
||||
* side. Note that Redis does not accept {@literal null} keys or values but can return null replies (for non-existing
|
||||
* keys).
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Costin Leau
|
||||
@@ -30,26 +31,8 @@ import org.springframework.util.ClassUtils;
|
||||
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}.
|
||||
* Obtain a {@link RedisSerializer} using java serialization. <strong>Note:</strong> Ensure that your domain objects
|
||||
* are actually {@link java.io.Serializable serializable}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 2.1
|
||||
@@ -59,7 +42,7 @@ public interface RedisSerializer<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link RedisSerializer} using java serialization with the given {@link ClassLoader}.<br />
|
||||
* Obtain a {@link RedisSerializer} using java serialization with the given {@link ClassLoader}.
|
||||
* <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}.
|
||||
@@ -102,10 +85,39 @@ public interface RedisSerializer<T> {
|
||||
return ByteArrayRedisSerializer.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the given object to binary data.
|
||||
*
|
||||
* @param value object to serialize. Can be {@literal null}.
|
||||
* @return the equivalent binary data. Can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
byte[] serialize(@Nullable T value) 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;
|
||||
|
||||
/**
|
||||
* Check whether the given value {@code type} can be serialized by this serializer.
|
||||
*
|
||||
* @param type the value type.
|
||||
* @return {@code true} if the value type can be serialized; {@code false} otherwise.
|
||||
*/
|
||||
default boolean canSerialize(Class<?> type) {
|
||||
return ClassUtils.isAssignable(getTargetType(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the serializer target type.
|
||||
*
|
||||
* @return the serializer target type.
|
||||
*/
|
||||
default Class<?> getTargetType() {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
@@ -81,13 +81,13 @@ public class StringRedisSerializer implements RedisSerializer<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deserialize(@Nullable byte[] bytes) {
|
||||
return (bytes == null ? null : new String(bytes, charset));
|
||||
public byte[] serialize(@Nullable String value) {
|
||||
return (value == null ? null : value.getBytes(charset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(@Nullable String string) {
|
||||
return (string == null ? null : string.getBytes(charset));
|
||||
public String deserialize(@Nullable byte[] bytes) {
|
||||
return (bytes == null ? null : new String(bytes, charset));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user