Skip to content

DDK Redis Starter ​

提供一个配好 JSON 序列化的 RedisTemplate<String, Object>,省掉每个项目重复写的那段序列化配置;反序列化只放行白名单里的类型。

能做什么 ​

  • 注册 RedisTemplate<String, Object>:key / hashKey 用字符串序列化,value / hashValue 用 GenericJacksonJsonRedisSerializer(Jackson 3),内部 JsonMapper 由 RedisJsonMapper 构造:
    • 按字段存取(字段可见、getter 不可见):领域对象不一定有 getter,getter 里也可能带计算逻辑;
    • 类型信息写进 @class 属性:除 JDK 自带的 final 标量(String、Long 等)、枚举、JsonNode 外都写类名,record 也能读回原类型(NON_FINAL 做不到这点,record 是 final 的);
    • 反序列化白名单:用 BasicPolymorphicTypeValidator 只放行三类包里的类型——
      1. JDK 常用包:java.lang、java.util、java.time、java.math(java.io、java.net 这类能触发 I/O 的包刻意不在内);
      2. 应用根包:@SpringBootApplication 所在包(取自 AutoConfigurationPackages),通常不用任何配置就能覆盖业务 DTO;
      3. ddk.redis.trusted-packages 显式补充的包。
    • 其余类型在读取时直接抛 SerializationException。任何能写 Redis 的人都无法再借 @class 让应用实例化 classpath 上的任意类;
    • findAndAddModules() 注册 JavaTimeModule 等模块,日期写成 ISO 字符串,忽略未知字段。
  • 不提供 RedisUtil 之类的工具类:早期版本有过一个逐个方法转发 RedisTemplate 的封装,覆盖面不全(没有 ZSet、scan、pipeline、Lua),还把可能为 null 的包装类型原样透传,已移除。直接注入 RedisTemplate<String, Object> 使用 opsForValue() / opsForHash() 等即可。
  • 依赖聚合:spring-boot-starter-data-redis(Lettuce 客户端)、spring-boot-starter-json、ddk-core。
    • 连接工厂、连接池、StringRedisTemplate 仍来自 Boot 的 RedisAutoConfiguration,本 starter 只接管 redisTemplate 这一个 Bean。
text
写入  User(record) ──► {"@class":"com.example.app.User","id":1,...}
读取  @class 在白名单内?  ── 是 ──► User
                         └─ 否 ──► SerializationException(不实例化)

引入方式 ​

xml
<dependency>
    <groupId>com.ddk</groupId>
    <artifactId>ddk-redis-starter</artifactId>
    <version>${ddk.version}</version>
</dependency>

import 了 ddk-dependencies BOM 之后 <version> 可以省略(见快速开始)。

配置项 ​

配置项类型默认值说明
ddk.redis.trusted-packagesList<String>空额外允许反序列化的包,按前缀匹配。JDK 常用包和应用根包已默认放行,这里只补充此外的包,例如独立模块里的共享 DTO
spring.data.redis.*-Boot 默认连接配置,由 Boot 的 RedisProperties 承载
spring.jackson.*-—对本 starter 无效。Redis 的 ObjectMapper 需要类型信息和白名单,与 Web 层的 JSON 行为本就不同,刻意不共用
yaml
ddk:
  redis:
    trusted-packages:
      - com.example.shared.dto

装配条件与降级行为 ​

  • 自动配置类:com.ddk.redis.starter.config.DdkRedisAutoConfiguration,@AutoConfiguration(before = RedisAutoConfiguration.class)——排在 Boot 之前,保证同名的 redisTemplate 由这里提供,而不是被 Boot 的 JDK 序列化版本抢先注册。
  • 条件:
    • @ConditionalOnClass(RedisTemplate.class);
    • redisTemplate:@ConditionalOnMissingBean(name = "redisTemplate"),应用自己定义同名 Bean 即可整体替换;
    • redisUtil:@ConditionalOnMissingBean。
  • Redis 服务不可达不会导致启动失败(Lettuce 懒连接),但每次读写都会抛 RedisConnectionFailureException 并传播给调用方,没有降级或熔断。

不适用的场景 / 已知问题 ​

  1. 类名写进了数据。类改名、换包后,Redis 里的老数据读不回来;跨服务共享同一个 key 时,读方必须有同名类。需要跨语言或长期存储的数据,应改用明确值类型的序列化器。按缓存名绑定值类型的方案见缓存设计推演。
  2. 标量不带类型信息。Long 小值读回来是 Integer,Float 读回来是 Double;取数值时按 Number 处理。
  3. 不可变集合读不回来。List.of() 的实现类是 java.util.ImmutableCollections$ListN,虽在白名单内,但没有可用的构造方式;存集合时用 ArrayList / HashMap。
  4. RedisTemplate 的返回值是可能为 null 的包装类型(Boolean hasKey、Long increment 等),自动拆箱有 NPE 风险。
  5. 没有分布式锁与限流,需要时自己实现或引 Redisson。
  6. 与 DDK Cache Starter 互不影响:缓存走 RedisCacheManager 自己的序列化配置,同一份数据在 @Cacheable 和 redisTemplate 之间不能互读。

文章以 CC BY-NC-SA 4.0 授权 · 代码片段以 MIT 授权