scaffold-redis -- Redis 缓存工具模块

模块概述

scaffold-redis 是基于 Spring Data Redis 封装的缓存工具模块,提供统一的 RedisCache 工具类,覆盖 String、List、Set、Hash、ZSet 五种数据结构的常用操作,内置自动 Key 前缀管理、空值防穿透、分布式锁原语等能力。引入此模块后自动装配,配合 scaffold.redis.* 配置前缀即可使用。

功能列表

  • 自动配置 RedisTemplate<String, Object>(Key 用 String 序列化,Value 用 JSON 序列化带类型信息)
  • 自动配置 StringRedisTemplate
  • 核心工具类 RedisCache:基本操作、List、Set、Hash、ZSet、计数器、分布式锁
  • 自动 Key 前缀管理(通过 scaffold.redis.keyPrefix 配置)
  • 空值缓存防穿透(可配置开关和过期时间)
  • 自定义 Key 生成器 RedisKeyGenerator(用于 @Cacheable 注解)
  • 缓存时间单位枚举 CacheTimeUnit(秒/分/时/天/周/月/年)

配置参数

application.yml 中以 scaffold.redis 为前缀配置:

scaffold:
  redis:
    enabled: true              # 是否启用模块
    cache-enabled: false       # 是否启用注解式缓存(@Cacheable 等)
    key-generator-enabled: false  # 是否启用自定义 Key 生成器
    key-prefix: "scaffold"     # 缓存 Key 前缀
    default-expire-time: 0     # 默认过期时间(秒),0 表示不设置
    cache-null-values: true    # 是否缓存空值(防穿透)
    null-value-expire-time: 60 # 空值缓存过期时间(秒)
    print-key-prefix: false    # 是否打印 Key 前缀(便于调试)
    key-serializer: STRING     # Key 序列化策略:STRING / JSON
    value-serializer: JSON     # Value 序列化策略:STRING / JSON / JDK
参数 类型 默认值 说明
enabled Boolean true 是否启用 Redis 模块
cache-enabled Boolean false 是否启用 Spring 注解式缓存
key-generator-enabled Boolean false 是否启用自定义 Key 生成器
key-prefix String "" 缓存 Key 前缀,设置后所有 Key 自动拼接 前缀:原始Key
default-expire-time Long 0 默认缓存过期时间(秒),0 表示不设置
cache-null-values Boolean true 是否缓存空值,防止缓存穿透
null-value-expire-time Long 60 空值缓存过期时间(秒)
print-key-prefix Boolean false 开发环境调试开关,打印生成的 Key
key-serializer Enum STRING Key 序列化策略
value-serializer Enum JSON Value 序列化策略

RedisCache 工具类方法说明

RedisCache 是核心工具类,通过构造函数注入即可使用:

@RequiredArgsConstructor
@Service
public class UserService {
    private final RedisCache redisCache;
}

基本操作

方法签名 说明
set(String key, Object value) 设置缓存(使用默认过期时间)
set(String key, Object value, Long timeout) 设置缓存(指定过期秒数,null 使用默认)
<T> T get(String key) 获取缓存,不存在返回 null
<T> T get(String key, Supplier<T> supplier) 获取缓存,不存在时回调获取并写入
<T> T get(String key, Supplier<T> supplier, Long timeout) 同上,可指定过期时间
delete(String key) 删除缓存
delete(Collection<String> keys) 批量删除
deleteByPattern(String pattern) 按模式删除(如 "user:*"
hasKey(String key) 判断缓存是否存在
expire(String key, long timeout) 设置过期时间(秒)
expire(String key, long timeout, TimeUnit unit) 设置过期时间(指定单位)
getExpire(String key) 获取剩余过期时间(秒),-1 永久,-2 不存在

分布式锁原语

方法签名 说明
setIfAbsent(String key, Object value, long timeout, TimeUnit unit) SETNX 操作,返回 true 表示获取锁成功

List 操作

方法签名 说明
leftPush(String key, Object value) 左侧入队
leftPop(String key) 左侧出队
rightPush(String key, Object value) 右侧入队
rightPop(String key) 右侧出队
range(String key, long start, long end) 获取指定范围元素
listSize(String key) 获取列表长度

Set 操作

方法签名 说明
add(String key, Object... values) 添加元素
isMember(String key, Object value) 判断元素是否存在
members(String key) 获取所有元素
setSize(String key) 获取集合大小
remove(String key, Object... values) 移除元素

Hash 操作

方法签名 说明
hSet(String key, String field, Object value) 设置哈希字段
hGet(String key, String field) 获取哈希字段值
hSetAll(String key, Map<String, Object> map) 批量设置哈希字段
hGetAll(String key) 获取所有哈希字段
hDelete(String key, Object... fields) 删除哈希字段
hHasKey(String key, String field) 判断哈希字段是否存在
hSize(String key) 获取哈希字段数量

ZSet 操作

方法签名 说明
zAdd(String key, Object value, double score) 添加元素(带分数)
rangeByScore(String key, double min, double max) 按分数范围获取元素
rank(String key, Object value) 获取元素排名
zRemove(String key, Object... values) 移除元素
zSize(String key) 获取有序集合大小

计数器操作

方法签名 说明
increment(String key, long delta) 自增(整数)
increment(String key, double delta) 自增(浮点数)
decrement(String key, long delta) 自减

其他

方法签名 说明
clear() 清空当前前缀下的所有缓存

Key 命名规范

模块使用 : 作为分隔符,完整的 Redis Key 格式为:

{keyPrefix}:{原始Key}

例如配置 key-prefix: scaffold 时:

scaffold:user:10001
scaffold:order:20260426001
scaffold:lock:seckill:100

建议业务模块按 模块:业务:标识 的结构组织 Key,例如:

user:info:10001          -- 用户信息缓存
order:detail:20260426001 -- 订单详情缓存
lock:seckill:100         -- 秒杀分布式锁

使用示例

基本缓存

@RequiredArgsConstructor
@Service
public class ProductService {

    private final RedisCache redisCache;

    public ProductVO getProduct(Long productId) {
        // 先查缓存,不存在则查库并写入缓存(30分钟)
        return redisCache.get("product:info:" + productId, () -> {
            Product product = productRepository.getById(productId);
            return BeanCopyUtils.copy(product, ProductVO.class);
        }, CacheTimeUnit.MINUTES.toSeconds(30));
    }

    public void updateProduct(ProductUpdateRequest request) {
        productRepository.updateById(BeanCopyUtils.copy(request, Product.class));
        // 更新后删除缓存
        redisCache.delete("product:info:" + request.getId());
    }
}

分布式锁

@RequiredArgsConstructor
@Service
public class SeckillService {

    private final RedisCache redisCache;

    public boolean seckill(Long userId, Long productId) {
        String lockKey = "lock:seckill:" + productId;
        Boolean locked = redisCache.setIfAbsent(lockKey, userId, 10, TimeUnit.SECONDS);
        if (!Boolean.TRUE.equals(locked)) {
            throw new BusinessException("操作太频繁,请稍后再试");
        }
        try {
            return doSeckill(userId, productId);
        } finally {
            redisCache.delete(lockKey);
        }
    }
}

缓存预热与批量清理

// 批量预热
Map<String, Object> batch = new HashMap<>();
for (Category cat : categories) {
    batch.put("category:" + cat.getId(), cat);
}
batch.forEach((key, value) -> redisCache.set(key, value, CacheTimeUnit.HOURS.toSeconds(2)));

// 按模式清理
redisCache.deleteByPattern("product:list:*");

计数器

// 原子扣减秒杀库存
Long remain = redisCache.decrement("seckill:stock:" + productId, 1);
if (remain < 0) {
    redisCache.increment("seckill:stock:" + productId, 1); // 回滚
    throw new BusinessException("库存不足");
}

CacheTimeUnit 枚举

枚举值 秒数 说明
SECONDS 1
MINUTES 60 分钟
HOURS 3600 小时
DAYS 86400
WEEKS 604800
MONTH 2592000 月(30天)
YEAR 31536000 年(365天)

提供 toSeconds(long amount) 方法用于换算,以及静态方法 forever()(返回 -1)和 notExpire()(返回 0)。

RedisConstant 常量

常量 说明
KEY_DELIMITER ":" Key 分隔符
PREFIX_DELIMITER ":" 前缀分隔符
EXPIRE_FOREVER -1L 永不过期
DO_NOT_EXPIRE 0L 不设置过期时间
NULL_VALUE "@NULL@" 空值缓存标识
LOCK_PREFIX "lock:" 分布式锁前缀
LOCK_DEFAULT_EXPIRE 30L 锁默认过期时间(秒)

自动配置说明

本模块通过 RedisAutoConfiguration 注册以下 Bean:

Bean 条件 说明
RedisTemplate<String, Object> classpath 有 RedisTemplate Key 用 String 序列化,Value 用 JSON(带类型信息)
StringRedisTemplate classpath 有 RedisTemplate 原生 String 操作
RedisCache 上述 Bean 均存在 核心工具类

自动配置通过 @ConditionalOnProperty(prefix = "scaffold.redis", name = "enabled", havingValue = "true", matchIfMissing = true) 控制,默认启用。

注意事项

  • get(key, supplier) 内部使用双重检查锁(synchronized)防止缓存击穿,适用于高并发场景。
  • 空值缓存默认启用(cacheNullValues: true),存储为 @NULL@ 标识,防止恶意请求穿透到数据库。
  • deleteByPattern 方法底层使用 KEYS 命令,在大数据量场景下可能阻塞 Redis,生产环境慎用。
  • clear() 方法依赖 keyPrefix 配置,未配置前缀时无法清空(会打印警告日志)。
  • 所有操作失败均抛出 RedisException,可在 Service 层捕获或由 GlobalExceptionHandler 兜底处理。