json - 如何使用 Spring Boot 2 CacheManager 在 Redis 中存储非类型化 JSON
问题描述
我在 Spring Boot 2 应用程序中使用 Redis 作为缓存存储。我@Cacheable
在某些方法中使用了注释,并且我想将 Redis 中的数据作为非类型化 JSON 存储。使用我当前的配置,保存数据可以正常工作,但读取它会生成ClassCastException
.
所有解决方案、答案、示例和教程都使用 JacksonObjectMapper
来配置RedisTemplate
或RedisCacheConfiguration
向 JSON 添加默认类型属性。这里的问题是这个缓存将由不同语言/技术的不同应用程序共享,我不能强迫其他应用程序像 Spring Boot 那样工作。
这是我现在拥有的:
配置
@Bean
CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory)
.cacheDefaults(cacheConfiguration())
.build();
}
private RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.serializeKeysWith(SerializationPair.fromSerializer(RedisSerializer.string()))
.serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(redisMapper())));
}
private ObjectMapper redisMapper() {
return new ObjectMapper()
//.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY)
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
}
服务
@Cacheable(key = "'persons::' + #id")
public Person getPerson(Long id) {
// return person from DB
}
当前配置的结果:
{
"name": "John",
"lastName": "Doe",
"age": 31
}
当 Spring 尝试使用反序列化器从缓存中读取内容时,它找不到"@class"
具有类型信息的属性,因此它返回一个LinkedHashMap
. 之后,CacheInterceptor
尝试将 a 转换LinkedHashMap
为 aPerson
并且发生这种ClassCastException
情况。
Request processing failed; nested exception is java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.test.dto.Person
在这一点上,如果我需要为要存储的每种类型编写一个序列化程序,或者我可以为所有人创建一个自定义序列化程序,我就可以了。到目前为止,我的研究一直没有成功。
谢谢你的时间。
解决方案
我现在也在和这个打架。甚至添加打字也无济于事,因为如果您@Cacheable
在返回“原始”类型的方法上使用Long
(即使在集合中),您也会Integer
回来,因为杰克逊使用智能数字映射,如果数字适合在那里,则返回 32 位整数。
是的,您现在可以使用 flag USE_LONG_FOR_INTS
,但这意味着它总是返回Long
,而不是首先放入缓存中的类型。
因此,如果您想将漂亮的 JSON 保存到 redis,则不能使用@Cacheable
,因为您可能Class cast exception
会经常遇到这种情况。
但是您可以手动使用缓存管理器,将 JSON 保存为纯文本并自己反序列化它,自己为对象映射器提供所需的返回类(不幸的是,注释不能自动为您做)。
示例代码(不完美且不处理来自对象映射器的异常):
@Autowired
private CacheManager cacheManager;
@Autowired
private ObjectMapper objectMapper;
public Person getPerson(Long id) {
Cache persons = cacheManager.getCache("persons");
if (persons == null) return // cache not exist, should not happen, return person from DB
return Optional.ofNullable(persons.get(id))
.map(Cache.ValueWrapper::get)
.map(o -> objectMapper.readerFor(Person.class).readValue(o))
.orElseGet(() -> {
Person p = // get person from DB
persons.put(id, objectMapper.writeValueAsString(p));
return p;
});
}
推荐阅读
- unity3d - 如何手动创建有效的蒙皮网格渲染器?
- r - 在公共服务器上的我的目录上安装 R 4.1.1,gcc 编译器
- javascript - curl文件上传和进度条
- c - 如何在c中将模式打印为函数
- list - 如何获取所有 Material Design 图标的列表?
- css - 拒绝应用样式,因为它的 MIME 类型 ('text/html') 不是受支持的样式表 MIME 类型,并且启用了严格的 MIME 检查
- python - 重置刽子手游戏中的字母,以便在游戏重新启动时再次可见
- r - 如何在ggplot中制作的barplot的阈值线下标记?
- java - 仅当 JpaRepository 为空时,如何生成实体 ID?
- reactjs - 使用功能组件对反应进行排序