文章

RedisTemplate 工具类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

@SuppressWarnings({"unchecked", "rawtypes", "ConstantConditions"})
@Component
public class RedisCache {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /* ------------------------- 通用操作 ------------------------- */

    /**
     * 设置缓存键的有效期(默认时间单位:秒)
     *
     * @param key     缓存键
     * @param timeout 有效期时长
     * @return 是否设置成功
     * @throws IllegalArgumentException 如果 key 为 null
     */
    public boolean expire(String key, long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置缓存键的有效期
     *
     * @param key      缓存键
     * @param timeout  有效期时长
     * @param timeUnit 时间单位
     * @return 是否设置成功
     * @throws IllegalArgumentException 如果 key 或 timeUnit 为 null
     */
    public boolean expire(String key, long timeout, TimeUnit timeUnit){validateKey(key);
        if (timeUnit == null) throw new IllegalArgumentException("TimeUnit must not be null");
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, timeUnit));
    }

    /**
     * 获取缓存键的剩余有效期(单位:秒)
     *
     * @param key 缓存键
     * @return 剩余有效期(秒),-2 表示键不存在,-1 表示永久有效
     */
    public long getExpire(String key) {validateKey(key);
        Long expire = redisTemplate.getExpire(key);
        return expire != null ? expire : -2;
    }

    /**
     * 检查缓存键是否存在
     *
     * @param key 缓存键
     * @return 是否存在
     * @throws IllegalArgumentException 如果 key 为 null
     */
    public boolean hasKey(String key) {validateKey(key);
        Boolean hasKey = redisTemplate.hasKey(key);
        return Boolean.TRUE.equals(hasKey);
    }

    /**
     * 删除单个缓存键
     *
     * @param key 缓存键
     * @return 是否删除成功
     */
    public boolean delete(String key) {validateKey(key);
        Boolean deleted = redisTemplate.delete(key);
        return Boolean.TRUE.equals(deleted);
    }

    /**
     * 批量删除缓存键
     *
     * @param keys 缓存键集合
     * @return 是否成功删除至少一个键
     * @throws IllegalArgumentException 如果 keys 为 null 或空
     */
    public boolean delete(Collection<String> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            throw new IllegalArgumentException("Keys collection must not be empty");}
        Long count = redisTemplate.delete(keys);
        return count != null && count > 0;
    }

    /**
     * 查找匹配模式的缓存键
     *
     * @param pattern 键匹配模式(如 "user:*")
     * @return 匹配的键集合(可能为空集合)
     * @throws IllegalArgumentException 如果 pattern 为 null
     */
    public Set<String> keys(String pattern) {
        if (pattern == null) throw new IllegalArgumentException("Pattern must not be null");
        return redisTemplate.keys(pattern);
    }

    /* ------------------------- String 操作 ------------------------- */

    /**
     * 缓存对象(永久有效)
     *
     * @param key   缓存键
     * @param value 缓存值
     * @throws IllegalArgumentException 如果 key 或 value 为 null
     */
    public <T> void set(String key, T value) {
        set(key, value, -1, TimeUnit.SECONDS);}

    /**
     * 缓存对象并设置有效期
     *
     * @param key      缓存键
     * @param value    缓存值
     * @param timeout  有效期时长(<=0 表示永久)
     * @param timeUnit 时间单位
     * @throws IllegalArgumentException 如果 key 或 value 为 null
     */
    public <T> void set(String key, T value, long timeout, TimeUnit timeUnit){validateKey(key);
        if (value == null) throw new IllegalArgumentException("Value must not be null");
        
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        if (timeout > 0){ops.set(key, value, timeout, timeUnit);
        } else {ops.set(key, value);
        }
    }

    /**
     * 获取缓存对象
     *
     * @param key 缓存键
     * @return 缓存值,不存在时返回 null
     * @throws IllegalArgumentException 如果 key 为 null
     */
    public <T> T get(String key) {validateKey(key);
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        return (T) ops.get(key);
    }

    /* ------------------------- Hash 操作 ------------------------- */

    /**
     * 缓存 Hash 结构数据
     *
     * @param key     缓存键
     * @param hashMap Hash 数据
     * @throws IllegalArgumentException 如果 key 或 hashMap 为 null
     */
    public <HK, HV> void setHash(String key, Map<HK, HV> hashMap) {validateKey(key);
        if (hashMap == null) throw new IllegalArgumentException("HashMap must not be null");
        redisTemplate.opsForHash().putAll(key, hashMap);
    }

    /**
     * 获取整个 Hash 结构
     *
     * @param key 缓存键
     * @return 完整的 Hash 数据(可能为空)
     */
    public <HK, HV> Map<HK, HV> getHash(String key) {validateKey(key);
        return (Map<HK, HV>) redisTemplate.opsForHash().entries(key);
    }

    /**
     * 向 Hash 中添加键值
     *
     * @param key   缓存键
     * @param hKey  Hash 键
     * @param value Hash 值
     */
    public <HK, HV> void putHashValue(String key, HK hKey, HV value) {validateKey(key);
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取 Hash 中的指定键值
     *
     * @param key  缓存键
     * @param hKey Hash 键
     * @return Hash 值,不存在时返回 null
     */
    public <HK, HV> HV getHashValue(String key, HK hKey) {validateKey(key);
        return (HV) redisTemplate.opsForHash().get(key, hKey);
    }

    /**
     * 删除 Hash 中的指定键
     *
     * @param key  缓存键
     * @param hKeys Hash 键集合
     * @return 成功删除的键数量
     */
    public <HK> long deleteHashKeys(String key, Collection<HK> hKeys) {validateKey(key);
        if (CollectionUtils.isEmpty(hKeys)) return 0;
        return redisTemplate.opsForHash().delete(key, hKeys.toArray());
    }

    /* ------------------------- List 操作 ------------------------- */

    /**
     * 缓存 List 数据
     *
     * @param key    缓存键
     * @param values 数据列表
     * @return 插入后的列表长度
     */
    public <T> long setList(String key, List<T> values) {validateKey(key);
        if (CollectionUtils.isEmpty(values)) return 0;
        Long size = redisTemplate.opsForList().rightPushAll(key, values);
        return size != null ? size : 0;
    }

    /**
     * 获取 List 数据
     *
     * @param key 缓存键
     * @param start 起始索引(包含)
     * @param end 结束索引(包含,-1 表示末尾)
     * @return 指定范围内的数据列表(可能为空)
     */
    public <T> List<T> getList(String key, long start, long end){validateKey(key);
        return (List<T>) redisTemplate.opsForList().range(key, start, end);
    }

    /* ------------------------- Set 操作 ------------------------- */

    /**
     * 缓存 Set 数据
     *
     * @param key    缓存键
     * @param values 数据集
     * @return 成功添加的元素数量
     */
    public <T> long addToSet(String key, Collection<T> values) {validateKey(key);
        if (CollectionUtils.isEmpty(values)) return 0;
        Long count = redisTemplate.opsForSet().add(key, values.toArray());
        return count != null ? count : 0;
    }

    /**
     * 获取整个 Set 数据
     *
     * @param key 缓存键
     * @return 数据集(可能为空)
     */
    public <T> Set<T> getSet(String key) {validateKey(key);
        return (Set<T>) redisTemplate.opsForSet().members(key);
    }

    /* ------------------------- 分布式锁 ------------------------- */

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey    锁键
     * @param clientId   客户端唯一标识
     * @param expireTime 锁的有效期
     * @param timeUnit   时间单位
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, String clientId, long expireTime, TimeUnit timeUnit){validateKey(lockKey);
        if (clientId == null || timeUnit == null) {
            throw new IllegalArgumentException("ClientId and timeUnit must not be null");}
        
        String script = "return redis.call('set', KEYS[1], ARGV[1],'NX','PX', ARGV[2])";
        RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
        String result = (String) redisTemplate.execute(
            redisScript,
            Collections.singletonList(lockKey),
            clientId,
            String.valueOf(timeUnit.toMillis(expireTime))
        );
        return "OK".equals(result);
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey  锁键
     * @param clientId 客户端唯一标识
     * @return 是否释放成功
     */
    public boolean releaseLock(String lockKey, String clientId) {validateKey(lockKey);
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long result = (Long) redisTemplate.execute(redisScript, Collections.singletonList(lockKey), clientId);
        return result != null && result == 1;
    }

    /* ------------------------- 参数校验 ------------------------- */

    private void validateKey(String key) {
        if (key == null || key.trim().isEmpty()) {
            throw new IllegalArgumentException("Redis key must not be null or empty");}
    }
}
License:  CC BY 4.0