Spring Data Redis对象缓存序列化问题解决

 更新时间:2024年01月04日 10:07:43   作者:冯文议  
相信在项目中,你一定是经常使用Redis,在使用时,有没有遇到同我一样,对象缓存序列化问题的呢,本文主要介绍了Spring Data Redis对象缓存序列化问题解决,感兴趣的可以了解一下

相信在项目中,你一定是经常使用 Redis ,那么,你是怎么使用的呢?在使用时,有没有遇到同我一样,对象缓存序列化问题的呢?那么,你又是如何解决的呢?

Redis 使用示例

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在应用启动如何添加启用缓存注解(@EnableCaching)。

假如我们有一个用户对象(UserVo):

@Data
public class UserVo implements Serializable {

    @Serial
    private static final long serialVersionUID = 2215423070276994378L;

    private Long id;

    private String name;

    private LocalDateTime createDateTime;

}

这里,我们实现了 Serializable 接口。

在我们需要缓存的方法上,使用 @Cacheable 注解,就表示如果返回的对象不是 null 时,就会对其进行缓存,下次查询,首先会去缓存中查询,查到了,就直接返回,不会再去数据库查询,查不到,再去数据库查询。

@Service
@Slf4j
public class UserServiceImpl implements IUserService {

    @Override
    @Cacheable(
            value = "sample-redis",
            key = "'user-'+#id",
            unless = "#result == null"
    )
    public UserVo getUserById(Long id) {

        log.info("userVo from db query");

        UserVo userVo = new UserVo();
        userVo.setId(1L);
        userVo.setName("Zhang San");
        userVo.setCreateDateTime(LocalDateTime.now());

        return userVo;
    }

}

核心代码:

@Cacheable(
        value = "sample-redis",
        key = "'user-'+#id",
        unless = "#result == null"
)

模拟测试,再写一个测试接口:

@RestController
@RequestMapping("/sample")
@RequiredArgsConstructor
@Slf4j
public class SampleController {

    private final IUserService userService;

    @GetMapping("/user/{id}")
    public UserVo getUserById(@PathVariable Long id) {

        UserVo vo = userService.getUserById(id);

        log.info("vo: {}", JacksonUtils.json(vo));

        return vo;
    }

}

我们再加上连接 redis 的配置:

spring:
  data:
    redis:
      host: localhost
      port: 6379

测试:

### getUserById
GET http://localhost:8080/sample/user/1

在这里插入图片描述

输出结果跟我们想的一样,第一次从数据库查,后面都从缓存直接返回。

总结一下:

  • 添加 spring-boot-starter-data-redis 依赖。

  • 使用启用缓存注解(@EnableCaching)。

  • 需要缓存的对象实现 Serializable 接口。

  • 使用 @Cacheable 注解缓存查询的结果。

遇到问题

在上面我们通过 spring boot 提供的 redis 实现了查询对象缓存这样一个功能,有下面几个问题:

  • 缓存的对象,必须序列化,不然会报错。
  • redis 存储的数据,看不懂,可以转成 json 格式吗?
  • 使用 Jackson 时,遇到特殊类型的字段会报错,比如 LocalDateTime。

第1个问题,如果对象没有实现 Serializable 接口,会报错:

在这里插入图片描述

关键信息:

java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [xxx.xxx.UserVo]

我详细描述一下第3个问题,默认是使用 Jdk序列化 JdkSerializationRedisSerializer,redis 里面存的数据如下:

在这里插入图片描述

问题很明显,对象必须要实现序列化接口,存的数据不易查看,所以,改用 GenericJackson2JsonRedisSerializer ,这就有了第3个问题。

我们加上下面的配置,就能解决第2个问题。

@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
    return RedisCacheConfiguration
            .defaultCacheConfig()
            .serializeValuesWith(
                    RedisSerializationContext
                            .SerializationPair
                            .fromSerializer(RedisSerializer.json())
            );
}

下面看第三个问题的错误:

在这里插入图片描述

如何解决?

既然有了明确的错误提示,那也是好解决的,我们可以这样:

@JsonDeserialize(using = LocalDateTimeDeserializer.class)		// 反序列化
@JsonSerialize(using = LocalDateTimeSerializer.class)		    // 序列化
private LocalDateTime createDateTime;

这样就可以了,我们看下redis里面存的数据:

{"@class":"com.fengwenyi.erwin.component.sample.redis.vo.UserVo","id":1,"name":"Zhang San","createDateTime":[2023,12,29,23,44,3,479011000]}

其实到这里,已经解决了问题,那有没有更省心的办法呢?

解决办法

其实我们知道,使用的就是 Jackson 进行 json 转换,而 json 转换,遇到 LocalDateTime 问题时,我们配置一下 module 就可以了,因为默认用的 SimpleModule,我们改用 JavaTimeModule 就可以了。

这时候问题又来啦,错误如下:

在这里插入图片描述

这时候存的数据如下:

{"id":1,"name":"Zhang San","createDateTime":"2023-12-29T23:31:52.548517"}

这就涉及到 Jackson 序列化漏洞的问题了,采用了白名单机制,我们就粗暴一点:

jsonMapper.activateDefaultTyping(
  LaissezFaireSubTypeValidator.instance, 
  ObjectMapper.DefaultTyping.NON_FINAL
);

redis 存的数据如下:

["com.fengwenyi.erwin.component.sample.redis.vo.UserVo",{"id":1,"name":"Zhang San","createDateTime":"2023-12-29T23:56:18.197203"}]

最后,来一段完整的 RedisCacheConfiguration 配置代码:

@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
    return RedisCacheConfiguration
            .defaultCacheConfig()
            .serializeValuesWith(
                    RedisSerializationContext
                            .SerializationPair
//                            .fromSerializer(RedisSerializer.json())
//                            .fromSerializer(
//                                    new GenericJackson2JsonRedisSerializer()
//                            )
                            .fromSerializer(redisSerializer())
            );
}

private RedisSerializer<Object> redisSerializer() {
    JsonMapper jsonMapper = new JsonMapper();
    JacksonUtils.configure(jsonMapper);
    jsonMapper.activateDefaultTyping(
            LaissezFaireSubTypeValidator.instance, 
            ObjectMapper.DefaultTyping.NON_FINAL
    );
    return new GenericJackson2JsonRedisSerializer(jsonMapper);
}

到此这篇关于Spring Data Redis对象缓存序列化问题解决的文章就介绍到这了,更多相关Spring Data Redis对象缓存序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java NIO实战之多人聊天室

    Java NIO实战之多人聊天室

    这篇文章主要为大家详细介绍了Java NIO实战之多人聊天室,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • 详解Java中JSON数据的生成与解析

    详解Java中JSON数据的生成与解析

    今天给大家带来的是关于Java的相关知识,文章围绕着Java中JSON数据的生成与解析展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • Java中SimpleDateFormat方法超详细分析

    Java中SimpleDateFormat方法超详细分析

    这篇文章主要给大家介绍了关于Java中SimpleDateFormat方法超详细分析的相关资料,SimpleDateFormat 是一个以国别敏感的方式格式化和分析数据的具体类,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-08-08
  • Java如何利用Socket传输信息或文件

    Java如何利用Socket传输信息或文件

    这篇文章主要介绍了Java如何利用Socket传输信息或文件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 使用Jenkins配置Git+Maven的自动化构建的方法

    使用Jenkins配置Git+Maven的自动化构建的方法

    这篇文章主要介绍了使用Jenkins配置Git+Maven的自动化构建的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01
  • Java中的线程同步全面讲解

    Java中的线程同步全面讲解

    线程同步指的是在多线程环境下,通过某种机制来协调多个线程对共享资源的访问,确保在同一时刻只有一个线程能够访问共享资源,从而避免数据不一致和其他并发问题,这篇文章主要介绍了Java中线程同步的相关资料,需要的朋友可以参考下
    2025-07-07
  • 详解Spring中BeanUtils工具类的使用

    详解Spring中BeanUtils工具类的使用

    这篇文章主要通过一些示例为大家详细介绍了Spring中BeanUtils工具类的使用,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-06-06
  • 详解MyBatis自定义Plugin插件

    详解MyBatis自定义Plugin插件

    这篇文章主要介绍了MyBatis自定义Plugin插件的相关知识,实现方法也很简单,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可,需要的朋友可以参考下
    2018-06-06
  • Java微服务无损发布生产实战案例及验证

    Java微服务无损发布生产实战案例及验证

    微服务架构是一项在云中部署应用和服务的新技术,这篇文章主要介绍了Java微服务无损发布生产实战及验证的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-10-10
  • 一文带你玩转Java异常处理

    一文带你玩转Java异常处理

    这篇文章主要为大家介绍一下Java中的异常处理机制,文中通过示例为大家进行了详细的介绍,对我们学习有一定的帮助,感兴趣的可以了解一下
    2022-08-08

最新评论