因为GenericJackson2JsonRedisSerializer默认启用DefaultTyping,会在JSON中自动添加@class字段以保留类型信息,用于正确反序列化;而Jackson2JsonRedisSerializer指定具体类型时则不加该字段。

为什么Jackson反序列化时会多出@class字段
因为默认用的是GenericJackson2JsonRedisSerializer,它启用了DefaultTyping.NON_FINAL(或类似策略),会在JSON里自动注入@class字段,记录原始Java类的全限定名,比如{"@class":"com.example.UserDTO","id":1,"name":"alice"}。这不是bug,是Jackson为支持泛型擦除后正确还原类型而设计的“类型信息嵌入”机制。
常见错误现象:Redis里看到的JSON多了@class,前端解析报错;或者换服务读取时因包名不一致导致反序列化失败;更糟的是,这个字段被当成业务字段透传给前端,暴露内部结构。
- 如果只在本服务内读写,且所有模块用同一套DTO类,
@class可接受但非必需 - 如果要跨语言(如Node.js读)、跨服务(不同包路径)、或对外提供缓存数据,必须禁用该行为
-
GenericJackson2JsonRedisSerializer默认启用类型信息;Jackson2JsonRedisSerializer(指定具体类型构造)则不会加@class
怎么禁用@class字段——三步清干净
核心是让Jackson不写类型信息,同时确保反序列化时仍能按预期类型还原。不要改ObjectMapper全局配置,只针对Redis序列化器定制:
- 声明
ObjectMapperBean时,调用disable(SerializationFeature.WRITE_CLASSNAME)和disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE) - 创建
Jackson2JsonRedisSerializer时,显式传入目标类(如new Jackson2JsonRedisSerializer(UserDTO.class)),而不是用泛型擦除版 - 若需支持多种DTO,别用
GenericJackson2JsonRedisSerializer,改用StringRedisTemplate+ 手动objectMapper.writeValueAsString(),把序列化/反序列化逻辑收口到工具类里
示例关键代码:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory, ObjectMapper objectMapper) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
// 关键:不用 GenericJackson2JsonRedisSerializer
Jackson2JsonRedisSerializer<Object> valueSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
valueSerializer.setObjectMapper(objectMapper);
template.setValueSerializer(valueSerializer);
template.afterPropertiesSet();
return template;
}注意:这里Object.class只是占位,实际存对象时仍需保证调用方明确类型,否则反序列化会变成LinkedHashMap。
为什么StringRedisTemplate没这个问题
因为StringRedisTemplate默认用StringRedisSerializer,它只做UTF-8编解码,不碰JSON、不调Jackson、不写任何元数据——你给什么字符串,它就原样存;你取出来,就是那个字符串。所以@class根本不会出现。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
适用场景很明确:当你已经自己用objectMapper.writeValueAsString(dto)转成JSON字符串再塞进Redis,或业务只要存纯字符串、数字、JSON文本,就直接用StringRedisTemplate,省心又安全。
- 别为了“统一用RedisTemplate”硬套
RedisTemplate<string object></string>,类型擦除+默认序列化器=隐式加@class - 如果DTO含
LocalDateTime等Java 8时间类型,记得在ObjectMapper里注册JavaTimeModule,否则即使禁了@class也会反序列化失败 - 检查Redis CLI里get出来的值:如果开头是
{"@class",说明序列化器没配对;如果是干净JSON,说明已生效
容易被忽略的兼容性陷阱
最麻烦的不是加@class,而是“一半加一半不加”——比如老服务用GenericJackson2JsonRedisSerializer存了带@class的数据,新服务换成Jackson2JsonRedisSerializer<userdto></userdto>去读,Jackson会因找不到对应类或类型不匹配静默失败,返回null或LinkedHashMap,而不是抛异常。
这种混合状态在线上查不到日志、测不出问题,直到某个接口突然返回空数据才暴露。所以升级序列化策略时,要么全量刷缓存,要么加一层兼容逻辑(比如先尝试按新方式反序列化,失败再 fallback 到带@class的老方式)。

















