常见的redis的序列化方式

概括

一般redis的序列化方式主要有:字符串序列化、json序列化、xml序列化、jdk序列化,具体可查阅org.springframework.data.redis.serializer.RedisSerializer 的实现类,其中对于json序列化,官方实现的是jackson的序列化,此外fastjson也有对应的实现。

源码分析

下面开始对spring-data-redis的源码稍加分析。

key、hash key的序列化一般使用如下方式:


...
/**
 * 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));
	}
}

可以看出使用的是字符串的序列化很常规

value、hash value的序列化一般使用如下方式:

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> {

// 三个构造函数
// 默认构造函数
public JdkSerializationRedisSerializer(){
this(new SerializingConverter(), new DeserializingConverter());}

// 指定ClassLoader的构造函数(热部署时可能需要用到,避免不同的类加载器搞混)
public JdkSerializationRedisSerializer(ClassLoader classLoader){...}

// 指定org.springframework.core.convert.converter.Converter的构造函数(严重依赖spring框架,也可以自己实现)
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);// 根据构造函数可知这里使用默认的序列化器
			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();
	}
...

以上可以知道常规的序列化方式;
接下来看反序列化方式:

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);// 根据构造函数可知这里使用默认的反序列化器
		}
		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());
			}
		}
	}
...
    
以上可以看出反序列化针对自定义传入Classloader的情况作了特殊处理

自定义序列化

通过分析源码后,如果不依赖于spring框架,我们可以自定义比较常规的序列化的方式:

字符串序列化:

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序列化:

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);
// 如果需要自定义ClassLoader,需要额外实现。               ObjectInputStream objectInputStream = new ConfigurableObjectInputStream(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);
        }
    }
}

 

转载于:https://my.oschina.net/wnjustdoit/blog/1860035

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值