redis 序列化 支持 PageImpl
2026/8/9 22:25:38 网站建设 项目流程

既然 Redis 序列化器配置了activateDefaultTyping,且不能修改,那就只能PageImpl具备被 Jackson 反序列化的能力


一、核心思路

text

Jackson 反序列化 PageImpl 失败的原因: ↓ PageImpl 没有无参构造器 ↓ 解决方案:为 PageImpl 添加反序列化支持(不修改 Redis 配置) ↓ 方式1:Jackson Mixin(推荐) 方式2:自定义反序列化器并注册到 Redis 的 ObjectMapper 方式3:在 Redis 配置中注入自定义 ObjectMapper

二、方案一:Jackson Mixin(推荐,最简洁)

通过 Mixin 为PageImpl提供@JsonCreator注解,告诉 Jackson 如何构造PageImpl对象。

1. 创建 Mixin 类

java

import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import java.util.List; /** * PageImpl 的 Jackson Mixin,用于提供反序列化支持 */ public abstract class PageImplMixin<T> { @JsonCreator public PageImplMixin( @JsonProperty("content") List<T> content, @JsonProperty("page") int page, @JsonProperty("size") int size, @JsonProperty("total") long total) { // Mixin 方法不需要实现,只用于注解 } // 如果 pageable 是完整对象,也可以支持 @JsonCreator public PageImplMixin( @JsonProperty("content") List<T> content, @JsonProperty("pageable") Pageable pageable, @JsonProperty("total") long total) { // 另一个构造方法签名 } }

2. 在 Redis 配置中注册 Mixin

找到 Redis 序列化器配置的地方,添加 Mixin:

java

@Configuration public class RedisConfig { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory factory) { // 1. 创建 ObjectMapper ObjectMapper mapper = new ObjectMapper(); // 2. 注册 PageImpl 的 Mixin mapper.addMixIn(PageImpl.class, PageImplMixin.class); // 3. 注册 JavaTimeModule(支持 LocalDate/LocalDateTime) mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 4. 保留原有的 DefaultTyping 配置 mapper.activateDefaultTyping( mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY ); // 5. 创建序列化器 Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); serializer.setObjectMapper(mapper); // 6. 配置 RedisCacheManager RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)) .entryTtl(Duration.ofHours(1)); return RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); } }

三、方案二:自定义反序列化器(更精细控制)

如果 Mixin 方式不生效,可以编写一个完整的PageImpl反序列化器。

1. 创建反序列化器

java

import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import java.io.IOException; import java.util.List; public class PageImplDeserializer extends StdDeserializer<PageImpl<?>> { public PageImplDeserializer() { super(PageImpl.class); } @Override public PageImpl<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); // 1. 解析 content JsonNode contentNode = node.get("content"); List<?> content = p.getCodec().treeToValue(contentNode, List.class); // 2. 解析 total JsonNode totalNode = node.get("total"); long total = totalNode != null ? totalNode.asLong() : 0L; // 3. 解析 pageable JsonNode pageableNode = node.get("pageable"); Pageable pageable; if (pageableNode == null || pageableNode.isNull()) { // 如果 pageable 不存在,从 page/size 构建 int page = node.has("page") ? node.get("page").asInt() : 0; int size = node.has("size") ? node.get("size").asInt() : 20; pageable = PageRequest.of(page, size); } else if (pageableNode.isTextual() && "INSTANCE".equals(pageableNode.asText())) { // 处理 Unpaged.INSTANCE pageable = Pageable.unpaged(); } else { // 从 pageable 对象解析 int page = pageableNode.has("pageNumber") ? pageableNode.get("pageNumber").asInt() : 0; int size = pageableNode.has("pageSize") ? pageableNode.get("pageSize").asInt() : 20; pageable = PageRequest.of(page, size); } return new PageImpl<>(content, pageable, total); } }

2. 注册到 Redis ObjectMapper

java

@Configuration public class RedisConfig { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory factory) { ObjectMapper mapper = new ObjectMapper(); // 注册 PageImpl 反序列化器 SimpleModule module = new SimpleModule(); module.addDeserializer(PageImpl.class, new PageImplDeserializer()); mapper.registerModule(module); // 其他配置... mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); mapper.activateDefaultTyping( mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY ); Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); serializer.setObjectMapper(mapper); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)) .entryTtl(Duration.ofHours(1)); return RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); } }

四、方案三:直接修改 Redis 配置类(如果可访问)

如果 Redis 配置类是可访问的,直接在你现有的RedisConfig中添加 Mixin:

java

@Configuration public class RedisConfig { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory factory) { // 获取现有的 ObjectMapper ObjectMapper mapper = new ObjectMapper(); // ✅ 添加这一行:注册 PageImpl 的 Mixin mapper.addMixIn(PageImpl.class, PageImplMixin.class); // 保留原有配置 mapper.activateDefaultTyping( mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY ); // ... 其他配置 } }

五、方案对比

方案改动量复杂度推荐度
Mixin小(新增1个类+1行配置)⭐⭐⭐⭐⭐
自定义反序列化器中(新增1个类+注册逻辑)⭐⭐⭐⭐
修改现有 Redis 配置最小(只加1行)最低⭐⭐⭐⭐⭐

六、最终推荐

使用 Mixin 方案,这是最简洁、最优雅的方式:

java

// 1. 创建 Mixin 类(只需要注解,不需要实现) public abstract class PageImplMixin<T> { @JsonCreator public PageImplMixin( @JsonProperty("content") List<T> content, @JsonProperty("page") int page, @JsonProperty("size") int size, @JsonProperty("total") long total) {} } // 2. 在 Redis 配置中注册 mapper.addMixIn(PageImpl.class, PageImplMixin.class);

这样:

  • ✅ 不需要修改 Redis 序列化器的activateDefaultTyping配置

  • ✅ 支持PageImpl的反序列化

  • ✅ 不影响其他类型

  • ✅ 改动量最小

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询