springboot redis 如何存放对象?

发布于 2022-09-12 22:16:45 字数 5622 浏览 14 评论 0

问题描述

springboot 2.3.7 想存取java对象到redis中去,但是能存不能取,并且存进去的值看起来不太对。

redis 5.0.4

问题出现的环境背景及自己尝试过哪些方法

开始用 springboot 2.2.2 版本,不行,升级到2.3.7后还是不行。
用Jedis 替换默认的 Lettuce 也不行
按照网上的教程配置 RedisConfig 配置类,来序列化对象,不行
自己手动写了个序列化工具类不行

已单独尝试 Jedis 和 Lettuce 进行对象的存取,没毛病

\#\#\# 相关代码

@Configuration
//@EnableCaching
//@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
    @Bean("redisTemplates")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 配置 json 序列化器 - Jackson2JsonRedisSerializer
        Jackson2JsonRedisSerializer jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        //ObjectMapper类是Jackson库的主要类。它提供一些功能将转换成Java对象匹配JSON结构
        ObjectMapper objectMapper = new ObjectMapper();
        //配置全局序列化参数
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSerializer.setObjectMapper(objectMapper);

        // 创建并配置自定义 RedisTemplateRedisOperator
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 将 key 序列化成字符串
        template.setKeySerializer(new StringRedisSerializer());
        // 将 hash 的 key 序列化成字符串
        template.setHashKeySerializer(new StringRedisSerializer());
        // 将 value 序列化成 json
//        template.setValueSerializer(jacksonSerializer);
        template.setValueSerializer(new RedisObjectSerializer());
        // 将 hash 的 value 序列化成 json
        template.setHashValueSerializer(new RedisObjectSerializer());
        template.afterPropertiesSet();
        return template;
    }

    class RedisObjectSerializer implements RedisSerializer<Object> {

        @Override
        public Object deserialize(byte[] bytes) {
            Object obj = SerializeUtil.unserialize(bytes);
            return obj;
        }

        @Override
        public byte[] serialize(Object object) {
            byte[] bytes = SerializeUtil.serialize(object);
            return bytes;
        }
    }
}

其中

template.setValueSerializer( jacksonSerializer);

不管是用自己的还是 jacksonSerializer都一样。

你期待的结果是什么?实际看到的错误信息又是什么?

跟了一下代码,发现存入redis之前序列化的byte[]大概长这样
image

存入之后,从redis中读取出来就变成这样了
image
明显就不是一个东西,所以在反序列化的时候,就会报一个不太相关的错误:

java.io.StreamCorruptedException: invalid stream header: 00000000
    at java.base/java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:946)
    at java.base/java.io.ObjectInputStream.<init>(ObjectInputStream.java:385)
    at cn.zj.service.util.SerializeUtil.unserialize(SerializeUtil.java:36)
    at cn.zj.service.config.RedisConfig$RedisObjectSerializer.deserialize(RedisConfig.java:117)
    at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:335)
    at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:61)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)
    at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96)
    at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:53)

如果我用 jacksonSerializer 的话,报错如下

17:09:18.860 logback [http-nio-8090-exec-5] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [/zj] threw exception [Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Illegal character ((CTRL-CHAR, code 0)): only regular white space (\r, \n, \t) is allowed between tokens
 at [Source: (byte[])""[truncated 604356 bytes]; line: 1, column: 2]; nested exception is com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 0)): only regular white space (\r, \n, \t) is allowed between tokens
 at [Source: (byte[])""[truncated 604356 bytes]; line: 1, column: 2]] with root cause
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 0)): only regular white space (\r, \n, \t) is allowed between tokens
 at [Source: (byte[])""[truncated 604356 bytes]; line: 1, column: 2]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1851)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:707)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:685)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2403)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:672)
    at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4664)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4513)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3572)
    at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.deserialize(Jackson2JsonRedisSerializer.java:73)

请教到底是该怎么存进去?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

无所谓啦 2022-09-19 22:16:45

遇到了一模一样的问题,存进去,取出来的时候前面会有0000 header数据 我这里可以看出来是原数据前面加了很多 0000 header

 @Bean(name = "redisTemplatePromote")
    public RedisTemplate<String, String> redisTemplatePromote() {

        RedisConnectionFactory factory = this.getRedisConnectionFactory(
                hostPromote, portPromote, 200, 1000, 0, 1000); // 建立Redis的连接
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer()); // key的序列化类型
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); // value的序列化类型
//        redisTemplate.setEnableDefaultSerializer(false);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

ValueSerializer 用 GenericFastJsonRedisSerializer可以成功存进去读取出来 但是用其他的ValueSerializer都不行,默认的也不行,但是我这里是迁移老代码,不能用新的序列化方式,否则其他项目中读不出来。搞得好郁闷。请问找到根本原因了吗?

兄弟 找到根本原因了
https://blog.csdn.net/WU45662...

掩饰不了的爱 2022-09-19 22:16:45

序列化和反序列化方式不一样
序列化用的jackson,反序列化用的对象流,肯定读不回来
配成一样的就行了

思慕 2022-09-19 22:16:45

如果是使用spring boot的redis template的话。可以把序列化读写方式都设为string。

之后读写对象的时候,都先把对象转换为json字符串去读写。

这样虽然多了一步处理并且不太优雅,但是确实实用,可以避免很多底层的坑。代码迁移性也很好

晨光如昨 2022-09-19 22:16:45

使用Jackson2JsonRedisSerializer指定为Value序列化器

Jackson2JsonRedisSerializer jackson2RedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

template.setValueSerializer(jackson2RedisSerializer);
溺深海 2022-09-19 22:16:45

其实我很想问题主的一个问题:题主是怎么使用这个RedisTemplate的呢?,当然我这个思路也许也不对,但是毕竟使用错误的话也有可能啊

因为从题主的配置来看,各种序列化工具都用完了,总之肯定不是想用RedisTemplate默认的序列化方式,而RedisTemplate默认的序列化器是JdkSerializationRedisSerializer

image.png

那它序列化出来的效果是什么呢?就是这种,我们看到也能阿巴阿巴阿巴的结果啊
image.png

所以我们才想要改成JSON格式的,我们人看得懂嘛,但是题主用了那么多都没有改成功,有没有想过
是不是你调用的时候根本用的就不是你配置的这个RedisTemplate,而是别人默认的RedisTemplate,默认的就是阿巴阿巴阿巴的效果了

我就算是只写这么几个配置
image.png

它最后出来的效果也是
image.png

我为啥这么想呢?因为我看到题主配置的RedisTemplate名叫redisTemplates
image.png

这是真为了说系统中配了两个RedisTemplate方便用?还是说配置错了,其实希望系统中只有一个RedisTemplate,毕竟人家默认的允许你覆盖的RedisTemplate名字就叫redisTemplate,是没有s的。

image.png

那假设是后者,题主若是使用的时候直接@Autowired走起,也不加@Qualifier限定,那就会出现怎么改序列化工具都不会生效的样子。毕竟使用的时候用的是默认的redisTemplate
image.png

当然由于不知道题主使用的时候到底怎么使用的,所以我这个也只是一个提醒吧,题主可以检查一下。实在还不行,题主可以回归简单,先去加简单的,像我那种一点点配置的(起码我证明了是没啥大问题的),看看有没有问题,最后再加到你想要的效果的配置

最后再提一下,如果真要序列化JSON最后也要反序列化出来的话,建议还是用GenericJackson2JsonRedisSerializer。刚Jackson2JsonRedisSerializer序列化之后的截图也看到了,其实里面只有属性,是没有任何关于这个属性是哪个Class信息的,所以反序列化之后是一个LinkedHashMap,若是强转你的Type是会失败的,若改为GenericJackson2JsonRedisSerializer之后,序列化结果就变成,可以看到很明显的区别了叭。
image.png

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文