浅谈在Java中对Redis序列化的配置

目录

一、为什么要对Redis进行序列化配置

二、实现步骤

1. 配置序列化器

2. 将序列化器配置到RedisTemplate中

三、代码简析

1. 序列化器

2. 装配到redisTemplate中

四、objectMapper的作用(了解)

五、总结


一、为什么要对Redis进行序列化配置

如果不配置的话,就会使用默认的Redis序列化方式,默认的Redis序列化是Java序列化方式
Redis默认序列化方式存在以下问题:



首先声明,Redis默认序列化方式就是直接使用Java的序列化方式,直接将Java对象转为二进制然后直接存储到Redis中。
1. Java序列化方式对类有要求,需要类实现序列化接口。因此对于还未实现序列化接口的第三方类无法进行序列化。
2. 不能进行数据传输。因为它是直接将对象转为二进制,然后保存在Redis中,这个二进制只能被Redis所接受,在Redis中存在解析算法,存进Redis中的遵循了Redis二进制规范,但是这个也只能被Redis所认识了,将数据展现到客户端时,可能因为不是JSON字符串序列化进去的,因此
反序列化展现的时候,并不是那么直接明了,可能看不懂,如下图所示,因为从Redis中拿出来的二进制是没有意义的,这个二进制只是遵循了Redis规范,而放到其它地方,别人是不认识的,所以是无法被解析的,既然无法被别人所解析,因此不能进行数据传输,传过去也没有意义,别人也不认识。
3. 可读性不强。正如我第二点所说的,因为不是JSON格式的二进制,因此不能很好的被解析,因此可读性不强。
4. 不易扩展。只支持Redis中已经存在的数据结构,默认的Redis序列化方式对于Redis中的数据结构存在优化,擅长存储Redis中已经定义好的数据结构,但是不支持其它数据结构,如果要序列化其它数据结构,就需要使用其它序列化方式,例如jackon、fastjson,这些序列化方式只需配置好
相关的配置,就能将对象都序列化JSON字符串格式,再放到Redis中以字符串进行存储就行了,其实就是将Redis中所不能存储的数据结构,转为可以被Redis存储的数据结构,如果硬要通过Redis默认的序列化方式序列化Redis所不支持的数据结构,那就需要修改Redis源码了,因此扩展性不好。



除了缺点,当然还是有优点的,有如下优点:
1. 首先就是简单。无需任何配置,拿来即用
2. 快速,效率高。因为是将对象直接转为二进制然后直接存储到Redis中的,而其它序列化方式可能还需要先将对象先转为字符串,然后还要转成字节数组,最后才将其转成二进制存入Redis中。
3. 适用于大型应用程序。其实是因为高效,因为大型应用程序对速度效率的要求较高。
4. 序列化的信息较多。因为不仅将属性进行序列化,同时将类中的方法等结构一并进行序列化了
既然Redis的默认序列化方式,针对不能进行数据传输,不能被其它编程语言所解析的致命缺点,因此就不得不谈其它序列化方式了,就是使用第三方的工具,比如说基于jackon、fastjosn等序列化框架进行序列化配置。

二、实现步骤

1. 配置序列化器

这个序列化器是基于fastjson框架

序列化器整体代码如下:

public class FastJson2JsonRedisSerializer implements RedisSerializer
{
    @SuppressWarnings("unused")
    private ObjectMapper objectMapper = new ObjectMapper();

    //配置序列化与反序列化字符集
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class clazz;

    //开启 fastjson 序列化库的自动类型支持功能,开启后,会在序列化或反序列化过程中自动添加类型信息,确保序列化与反序列化成功
    static
    {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    }

    public FastJson2JsonRedisSerializer(Class clazz)
    {
        super();
        this.clazz = clazz;
    }

    //序列化过程,使用fastjson将对象转为字节数组
    @Override
    public byte[] serialize(T t) throws SerializationException
    {
        if (t == null)
        {
            return new byte[0];
        }
        //添加SerializerFeature.WriteClassName可以在序列化时,添加类型信息
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    //反序列化过程,将字节数组转为Java对象
    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    {
        if (bytes == null || bytes.length <= 0)
        {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);

        //解析为clazz类型的对象
        return JSON.parseObject(str, clazz);
    }

    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. 将序列化器配置到RedisTemplate中

整体代码如下:

@Configuration
public class RedisConfig {

        @Bean
        @SuppressWarnings(value = { "unchecked", "rawtypes" })
        public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory)
        {
            RedisTemplate template = new RedisTemplate<>();
            template.setConnectionFactory(connectionFactory);

            FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);

            ObjectMapper mapper = new ObjectMapper();
            mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
            serializer.setObjectMapper(mapper);

            // 使用StringRedisSerializer来序列化和反序列化redis的key值
            template.setKeySerializer(new StringRedisSerializer());
            template.setValueSerializer(serializer);

            // Hash的key也采用StringRedisSerializer的序列化方式
            template.setHashKeySerializer(new StringRedisSerializer());
            template.setHashValueSerializer(serializer);

            template.afterPropertiesSet();
            return template;
        }
}

三、代码简析

1. 序列化器

1. 序列化器需要实现RedisSerializer接口。因为这为了和默认的序列化器一样,都遵循着这个接口规范,当以后将这个序列化器注入到redisTemplate中,redisTemplate也遵循了这套接口规范,直接调用这个接口中的序列化和反序列化方法。
2. 配置序列化与反序列化时的字符集。public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");当序列化的时候,将JSON字符串转为字节数组的时候需要指定字符集,而反序列化时,将字节数组转为JSON字符串时,同样需要指定字符集,也就是说编码与解码的过程都需要遵循同一种字符集。
3. 开启fastjosn序列化库的自动类型支持功能ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,就是在序列化的时候,自动加入序列化类的类型信息,只有这样才能确保反序列化成功。当然这里在JSON.toJSONString(t, SerializerFeature.WriteClassName)序列化的时候,构造器参数得提供SerializerFeature.WriteClassName,才能在序列化的时候正确加入对象类型信息,这个可以通过反序列化工具清晰的看到。
4. 重写serialize、deserialize两个方法,这一步最为重要,核心步骤。也就是接口中定义的序列化与反序列化方法,序列化过程是将对象转成JSON字符串然后再转成字节数组,而反序列化过程就是将字节数组转为JSON字符串,然后再解析成对象。
5. 设置对象映射器方法(可选)
下面我来简单说一下objectMapper对象映射器的作用,其实一般是不需要配置这个的,一般情况下配置了我们自定义的序列化器就能完成常见的对象的序列化与反序列化,对于复杂对象就可能要用到。

2. 装配到redisTemplate中

1. 使用StringRedisSerializer来序列化和反序列化redis的key值,以及hash结构中的key
2. 使用自定义序列化器来序列化和反序列化redis中的value值,以及hash结构中的value
3. 配置objectMapper,设置相关属性(可选)。

四、objectMapper的作用(了解)

1.  序列化Java对象
2.  反序列化JSON字符串
3.  设置序列化和反序列化的属性
4.  处理JSON字符串
总的来说,objectMapper就是用来实现对象与JSON字符串之间相互转换的一个类,序列化复杂类型对象时,可能用得到,而因为其实在这方面fastjson已经做的比较好了,因此一般不需要再进行设置,在我的博客项目中,发现还是需要配置一下这个的。

五、总结

下面来总结一下使用基于fastjson序列化框架搭建的自定义序列化器,相比较原来的Redis默认序列化方式的优点:
1. 可以进行数据传输。最重要的使用JSON字符串作为序列化与反序列化的中间媒介,便于与其它编程语言进行数据传输
2. 提供了自动类型引入机制。可以在序列化时,自动添加类型信息,以提高序列化与反序列化的正确率,配合redisTemplate进行使用,就可以直接进行类型转换,因为是泛型方法,强转都不需要了。
3.fastjson框架能自动解决循环引用,重复引用的问题。

说了这么多,其实就是一个通用的配置,cv就行了,当然对于复杂的类型对象,还是需要在序列化器中额外加一些配置的(比如objectMapper的配置),关键是学会基于第三方框架,完成自定义序列化配置。

你可能感兴趣的:(SpringBoot,Redis,redis,java,数据库,spring,boot,json)