Generalization
The main serialization methods of redis are string serialization, json serialization, xml serialization, jdk serialization. The implementation classes of org.springframework.data.redis.serializer.RedisSerializer can be consulted. For json serialization, jackson serialization is the official implementation, and fastjson has corresponding implementation.
Source Code Analysis
A little bit more analysis of the source code for spring-data-redis,
The serialization of key and hash key is generally done as follows:
... /** * Simple String to byte[] (and back) serializer. Converts Strings into bytes and vice-versa using the specified charset * (by default UTF-8). * <p> * Useful when the interaction with the Redis happens mainly through Strings. * <p> * Does not perform any null conversion since empty strings are valid keys/values. * * @author Costin Leau * @author Christoph Strobl */ public class StringRedisSerializer implements RedisSerializer<String> { private final Charset charset; public StringRedisSerializer() { this(Charset.forName("UTF8")); } public StringRedisSerializer(Charset charset) { Assert.notNull(charset, "Charset must not be null!"); this.charset = charset; } public String deserialize(byte[] bytes) { return (bytes == null ? null : new String(bytes, charset)); } public byte[] serialize(String string) { return (string == null ? null : string.getBytes(charset)); } } //You can see that string serialization is common
The serialization of value and hash value is typically done in the following ways:
part I ... /** * Java Serialization Redis serializer. Delegates to the default (Java based) {@link DefaultSerializer serializer} and * {@link DefaultDeserializer}. This {@link RedisSerializer serializer} can be constructed with either custom * {@link ClassLoader} or own {@link Converter converters}. */ public class JdkSerializationRedisSerializer implements RedisSerializer<Object> { // Three constructors // Default constructor public JdkSerializationRedisSerializer(){ this(new SerializingConverter(), new DeserializingConverter());} // Specify the constructor for ClassLoader (which may be required for hot deployment to avoid confusion between different class loaders) public JdkSerializationRedisSerializer(ClassLoader classLoader){...} // Specify the constructor for org.springframework.core.convert.converter.Converter (heavily dependent on the spring framework or self-implemented) public JdkSerializationRedisSerializer(Converter<Object, byte[]> serializer, Converter<byte[], Object> deserializer){...}; ... part II ... org.springframework.core.serializer.support.SerializingConverter#convert /** * Serializes the source object and returns the byte array result. */ @Override public byte[] convert(Object source) { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024); try { this.serializer.serialize(source, byteStream);// According to the constructor, the default serializer is used here return byteStream.toByteArray(); } catch (Throwable ex) { throw new SerializationFailedException("Failed to serialize object using " + this.serializer.getClass().getSimpleName(), ex); } } ... part III ... org.springframework.core.serializer.DefaultSerializer#serialize /** * 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(); } ... //The general serialization method is known above. //Next, look at how to deserialize: part IV ... org.springframework.core.serializer.support.DeserializingConverter#convert public Object convert(byte[] source) { ByteArrayInputStream byteStream = new ByteArrayInputStream(source); try { return this.deserializer.deserialize(byteStream);// From the constructor, the default deserializer is used here } catch (Throwable ex) { throw new SerializationFailedException("Failed to deserialize payload. " + "Is the byte array a result of corresponding serialization for " + this.deserializer.getClass().getSimpleName() + "?", ex); } } ... part V ... org.springframework.core.serializer.DefaultDeserializer#deserialize /** * Read from the supplied {@code InputStream} and deserialize the contents * into an object. * @see ObjectInputStream#readObject() */ @Override @SuppressWarnings("resource") public Object deserialize(InputStream inputStream) throws IOException { ObjectInputStream objectInputStream = new ConfigurableObjectInputStream(inputStream, this.classLoader); try { return objectInputStream.readObject(); } catch (ClassNotFoundException ex) { throw new NestedIOException("Failed to deserialize object type", ex); } } ... part VI ... /** * Special ObjectInputStream subclass that resolves class names * against a specific ClassLoader. Serves as base class for * {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream}. * * @author Juergen Hoeller * @since 2.5.5 */ public class ConfigurableObjectInputStream extends ObjectInputStream { ... @Override protected Class<?> resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { try { if (this.classLoader != null) { // Use the specified ClassLoader to resolve local classes. return ClassUtils.forName(classDesc.getName(), this.classLoader); } else { // Use the default ClassLoader... return super.resolveClass(classDesc); } } catch (ClassNotFoundException ex) { return resolveFallbackIfPossible(classDesc.getName(), ex); } } @Override protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { if (!this.acceptProxyClasses) { throw new NotSerializableException("Not allowed to accept serialized proxy classes"); } if (this.classLoader != null) { // Use the specified ClassLoader to resolve local proxy classes. Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { try { resolvedInterfaces[i] = ClassUtils.forName(interfaces[i], this.classLoader); } catch (ClassNotFoundException ex) { resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex); } } try { return ClassUtils.createCompositeInterface(resolvedInterfaces, this.classLoader); } catch (IllegalArgumentException ex) { throw new ClassNotFoundException(null, ex); } } else { // Use ObjectInputStream's default ClassLoader... try { return super.resolveProxyClass(interfaces); } catch (ClassNotFoundException ex) { Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex); } return ClassUtils.createCompositeInterface(resolvedInterfaces, getFallbackClassLoader()); } } } ... //As you can see above, deserialization has special handling for custom incoming Classloader cases
Custom Serialization
After analyzing the source code, we can customize a more general way of serialization if we don't rely on the spring framework:
String serialization:
package com.caiya.test.serialization.jdk; import com.caiya.test.serialization.Serializer; import com.caiya.test.serialization.exception.SerializationException; import java.nio.charset.Charset; /** * Simple String to byte[] (and back) serializer. Converts Strings into bytes and vice-versa using the specified charset * (by default UTF-8). * <p/> * Useful when the interaction with the Redis happens mainly through Strings. * <p/> * Does not perform any null conversion since empty strings are valid keys/values. * * @author wangnan * @since 1.0 */ public class StringSerializer implements Serializer<String> { private final Charset charset; public StringSerializer() { this(Charset.forName("UTF-8")); } public StringSerializer(Charset charset) { if (charset == null) throw new IllegalArgumentException("Charset must not be null!"); this.charset = charset; } @Override public byte[] serialize(String string) throws SerializationException { return (string == null ? null : string.getBytes(charset)); } @Override public String deserialize(byte[] bytes) throws SerializationException { return (bytes == null ? null : new String(bytes, charset)); } }
jdk serialization:
package com.caiya.test.serialization.jdk; import com.caiya.test.serialization.Serializer; import com.caiya.test.serialization.exception.NestedIOException; import com.caiya.test.serialization.exception.SerializationException; import com.caiya.test.serialization.exception.SerializationFailedException; import java.io.*; /** * Java Serialization serializer. * * @author wangnan * @since 1.0 */ public class JdkSerializationSerializer implements Serializer<Object> { private final ClassLoader classLoader; public JdkSerializationSerializer() { this.classLoader = null; } public JdkSerializationSerializer(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public byte[] serialize(Object object) throws SerializationException { if (object == null) { return SerializationUtils.EMPTY_ARRAY; } try { if (!(object instanceof Serializable)) { throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " + "but received an object of type [" + object.getClass().getName() + "]"); } ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream); objectOutputStream.writeObject(object); objectOutputStream.flush(); return byteStream.toByteArray(); } catch (Exception ex) { throw new SerializationException("Cannot serialize", ex); } } @Override public Object deserialize(byte[] bytes) throws SerializationException { if (SerializationUtils.isEmpty(bytes)) { return null; } try { ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes); try { ObjectInputStream objectInputStream = new ObjectInputStream(byteStream); // If you need to customize ClassLoader, you need an additional implementation.ObjectInputStream objectInputStream = new Configurable ObjectInputStream (byteStream, this.classLoader); try { return objectInputStream.readObject(); } catch (ClassNotFoundException ex) { throw new NestedIOException("Failed to deserialize object type", ex); } } catch (Throwable ex) { throw new SerializationFailedException("Failed to deserialize payload. " + "Is the byte array a result of corresponding serialization for " + this.getClass().getSimpleName() + "?", ex); } } catch (Exception ex) { throw new SerializationException("Cannot deserialize", ex); } } }