You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 09:20:55