Spring Boot Redis缓存反序列化报错:Missing type id property '@class'
Spring Boot Redis缓存反序列化报错:Missing type id when trying to resolve subtype of java.lang.Object
问题场景
我在Spring Boot项目中实现Redis缓存,用于缓存方法返回的LoyaltyClass数据。首次执行方法时数据成功存入缓存,但第二次从缓存读取时抛出序列化异常。
缓存方法代码:
@Cacheable(value = "ProgramCache", key="#programId") public LoyaltyClass findProgramFromGoogle(String programId) { try { LoyaltyClass loyaltyClass = AuthenticationService.service.loyaltyclass().get(generateClassId(programId)).execute(); logger.info("Program fetched {}", programId); return loyaltyClass; } catch (GoogleJsonResponseException ex) { if (ex.getStatusCode() != 404) { // 其他错误处理 logger.info("Error while fetching program from google {} ", ex); throw new ClientErrorException(); } // 未注册异常 throw new ProgramNotRegisteredException(); }catch (IOException e) { logger.info("Error while fetching class {} \n {} ", e); throw new ClientErrorException(); } }
报错信息
2024-07-05 15:35:06.494 ERROR 23172 --- [nio-8652-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/googlewallet] threw exception [Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class' at [Source: (byte[])"{}"; line: 1, column: 2]; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class' at [Source: (byte[])"{}"; line: 1, column: 2]] with root cause com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class' at [Source: (byte[])"{}"; line: 1, column: 2] at com.fasterxml.jackson.databind.exc.InvalidTypeIdException.from(InvalidTypeIdException.java:43) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.DeserializationContext.missingTypeIdException(DeserializationContext.java:1794) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.DeserializationContext.handleMissingTypeId(DeserializationContext.java:1323) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._handleMissingTypeId(TypeDeserializerBase.java:302) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedUsingDefaultImpl(AsPropertyTypeDeserializer.java:166) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:107) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:195) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:710) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4526) ~[jackson-databind-2.11.4.jar:2.11.4] at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3529) ~[jackson-databind-2.11.4.jar:2.11.4] at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:150) ~[spring-data-redis-2.4.5.jar:2.4.5] at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:130) ~[spring-data-redis-2.4.5.jar:2.4.5] at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:49) ~[spring-data-redis-2.4.5.jar:2.4.5] at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:272) ~[spring-data-redis-2.4.5.jar:2.4.5] at org.springframework.data.redis.cache.RedisCache.deserializeCacheValue(RedisCache.java:280) ~[spring-data-redis-2.4.5.jar:2.4.5] at org.springframework.data.redis.cache.RedisCache.lookup(RedisCache.java:94) ~[spring-data-redis-2.4.5.jar:2.4.5] at org.springframework.cache.support.AbstractValueAdaptingCache.get(AbstractValueAdaptingCache.java:58) ~[spring-context-5.3.4.jar:5.3.4] at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:73) ~[spring-context-5.3.4.jar:5.3.4] at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:571) ~[spring-context-5.3.4.jar:5.3.4] at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:536) ~[spring-context-5.3.4.jar:5.3.4] at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:402) ~[spring-context-5.3.4.jar:5.3.4] at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345) ~[spring-context-5.3.4.jar:5.3.4] at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:64) ~[spring-context-5.3.4.jar:5.3.4] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.4.jar:5.3.4] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.4.jar:5.3.4] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) ~[spring-aop-5.3.4.jar:5.3.4]
当前Redis配置
@Configuration public class RedisConfig { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { RedisSerializer<Object> serializer = new GenericJackson2JsonRedisSerializer(objectMapper()); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)); return RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(config) .build(); } @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.activateDefaultTyping( BasicPolymorphicTypeValidator.builder() .allowIfBaseType(Object.class) .build(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY ); return objectMapper; } }
问题原因与解决方法
1. 清理旧缓存数据
首次配置自定义ObjectMapper前存入的缓存数据(示例中为{})没有@class类型标识,现在启用类型校验后无法解析。需要先删除Redis中ProgramCache相关的缓存key,确保后续存入的数据包含类型信息。
2. 优化ObjectMapper类型校验范围
当前配置允许所有Object的子类进行类型序列化,范围过宽,容易引发解析问题。建议针对缓存的具体类型(如LoyaltyClass)缩小校验范围:
@Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); // 仅允许LoyaltyClass及其子类进行类型校验 BasicPolymorphicTypeValidator typeValidator = BasicPolymorphicTypeValidator.builder() .allowIfBaseType(LoyaltyClass.class) .build(); objectMapper.activateDefaultTyping( typeValidator, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY ); return objectMapper; }
3. 确认缓存配置生效
检查项目中是否存在其他Redis缓存配置类,避免当前配置被覆盖。确保RedisCacheManager确实使用了自定义的GenericJackson2JsonRedisSerializer。
内容的提问来源于stack exchange,提问作者Muddassir Rahman
相关产品推荐
相关产品推荐

