java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot RedisTemplate

SpringBoot集成RedisTemplate的实现示例

作者:IT橘子皮

本文主要介绍了SpringBoot集成RedisTemplate的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、📦 基础配置与工具类

1. Maven依赖 (pom.xml)

<dependencies>
    <!-- Spring Boot Redis Starter 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- 其他依赖... -->
</dependencies>

2. 应用配置 (application.yml)

spring:
  redis:
    host: localhost           # Redis服务器地址
    port: 6379                # Redis服务器端口
    password:                 # Redis访问密码,如果没有密码则不配置
    database: 0               # Redis数据库索引(0-15)
    lettuce:
      pool:
        max-active: 8         # 连接池最大连接数
        max-idle: 8           # 连接池最大空闲连接数
        min-idle: 0           # 连接池最小空闲连接数
        max-wait: 200ms       # 获取连接的最大等待时间
      shutdown-timeout: 100ms # 关闭超时时间
    timeout: 2000ms           # 连接超时时间

3. Redis配置类 (RedisConfig.java)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

/**
 * Redis配置类
 * 配置RedisTemplate的序列化方式,解决键值对存储的乱码问题
 * 
 * @author Developer
 * @version 1.0
 * @since 2025-09-22
 */
@Configuration
public class RedisConfig {

    /**
     * 配置RedisTemplate Bean
     * 设置key和value的序列化方式,避免存储乱码
     *
     * @param connectionFactory Redis连接工厂,由Spring自动注入
     * @return 配置好的RedisTemplate实例
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        
        // 设置连接工厂
        template.setConnectionFactory(connectionFactory);
        
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

4. Redis通用工具类 (RedisUtil.java)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 * 封装常用Redis操作,提供便捷的API调用方式
 * 
 * @author Developer
 * @version 1.0
 * @since 2025-09-22
 */
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // ============================== 通用方法 ==============================
    
    /**
     * 删除键
     *
     * @param key 要删除的键
     * @return true表示删除成功,false表示失败
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 判断键是否存在
     *
     * @param key 要检查的键
     * @return true表示存在,false表示不存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置键的过期时间
     *
     * @param key 键
     * @param timeout 过期时间
     * @param unit 时间单位
     * @return true表示设置成功,false表示失败
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获取键的剩余生存时间(秒)
     *
     * @param key 键
     * @return 剩余生存时间(秒),-1表示永不过期,-2表示键不存在
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    // ============================== String操作 ==============================
    
    /**
     * 设置键值对
     *
     * @param key 键
     * @param value 值
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 设置键值对并指定过期时间
     *
     * @param key 键
     * @param value 值
     * @param timeout 过期时间
     * @param unit 时间单位
     */
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 只有在键不存在时设置键值对(分布式锁基础实现)
     *
     * @param key 键
     * @param value 值
     * @param timeout 过期时间
     * @param unit 时间单位
     * @return true表示设置成功,false表示键已存在
     */
    public Boolean setIfAbsent(String key, Object value, long timeout, TimeUnit unit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
    }

    /**
     * 获取键的值
     *
     * @param key 键
     * @return 对应的值,键不存在时返回null
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 将键中存储的数字值增一(原子操作)
     *
     * @param key 键
     * @param delta 增量,可以为负值
     * @return 增减后的值
     */
    public Long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    // ============================== Hash操作 ==============================
    
    /**
     * 向Hash中添加单个字段
     *
     * @param key Hash键
     * @param hashKey Hash字段键
     * @param value 字段值
     */
    public void hPut(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 向Hash中添加多个字段
     *
     * @param key Hash键
     * @param map 字段映射表
     */
    public void hPutAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 获取Hash中指定字段的值
     *
     * @param key Hash键
     * @param hashKey Hash字段键
     * @return 字段值,字段不存在时返回null
     */
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    /**
     * 获取Hash中所有字段和值
     *
     * @param key Hash键
     * @return 包含所有字段和值的Map
     */
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 删除Hash中的一个或多个字段
     *
     * @param key Hash键
     * @param hashKeys 要删除的字段键
     * @return 删除的字段数量
     */
    public Long hDelete(String key, Object... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }

    // ============================== List操作 ==============================
    
    /**
     * 向列表左侧添加元素
     *
     * @param key 列表键
     * @param value 要添加的元素
     * @return 添加后列表的长度
     */
    public Long lPush(String key, Object value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 获取列表指定范围内的元素
     *
     * @param key 列表键
     * @param start 起始索引(0表示第一个元素)
     * @param end 结束索引(-1表示最后一个元素)
     * @return 元素列表
     */
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 从列表左侧弹出元素
     *
     * @param key 列表键
     * @return 弹出的元素,列表为空时返回null
     */
    public Object lPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    // ============================== Set操作 ==============================
    
    /**
     * 向集合中添加一个或多个元素
     *
     * @param key 集合键
     * @param values 要添加的元素
     * @return 成功添加的元素数量(忽略已存在的元素)
     */
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * 获取集合中的所有元素
     *
     * @param key 集合键
     * @return 包含所有元素的Set
     */
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 判断元素是否在集合中
     *
     * @param key 集合键
     * @param value 要检查的元素
     * @return true表示元素存在,false表示不存在
     */
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    // ============================== ZSet操作 ==============================
    
    /**
     * 向有序集合中添加元素
     *
     * @param key 有序集合键
     * @param value 元素值
     * @param score 元素分数(用于排序)
     * @return true表示添加成功,false表示元素已存在(更新分数)
     */
    public Boolean zAdd(String key, Object value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 获取有序集合指定排名范围内的元素(按分数升序)
     *
     * @param key 有序集合键
     * @param start 起始排名(0表示第一名)
     * @param end 结束排名(-1表示最后一名)
     * @return 元素集合
     */
    public Set<Object> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 获取有序集合指定排名范围内的元素及其分数(按分数降序)
     *
     * @param key 有序集合键
     * @param start 起始排名(0表示第一名)
     * @param end 结束排名(-1表示最后一名)
     * @return 包含元素和分数的TypedTuple集合
     */
    public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    /**
     * 增加有序集合中元素的分数
     *
     * @param key 有序集合键
     * @param value 元素值
     * @param delta 增量
     * @return 增加后的分数
     */
    public Double zIncrementScore(String key, Object value, double delta) {
        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }
}

二、🚀 实战应用

1. 分布式锁服务 (DistributedLockService.java)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁服务
 * 基于Redis实现分布式锁,保证分布式环境下资源访问的互斥性
 * 使用Lua脚本保证锁释放操作的原子性
 * 
 * @author Developer
 * @version 1.0
 */
@Service
public class DistributedLockService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // Lua脚本:只有锁的值与传入的请求标识匹配时才删除锁(保证原子性)
    private static final String RELEASE_LOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "return redis.call('del', KEYS[1]) " +
        "else " +
        "return 0 " +
        "end";

    private static final String LOCK_PREFIX = "lock:";

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey 锁的业务键(会自动添加前缀)
     * @param requestId 请求标识(需保证全局唯一)
     * @param expireTime 锁的过期时间(防止死锁)
     * @param unit 时间单位
     * @return true表示获取锁成功,false表示失败
     */
    public Boolean tryLock(String lockKey, String requestId, long expireTime, TimeUnit unit) {
        String fullLockKey = LOCK_PREFIX + lockKey;
        return redisTemplate.opsForValue().setIfAbsent(fullLockKey, requestId, expireTime, unit);
    }

    /**
     * 释放分布式锁
     * 使用Lua脚本保证检查锁归属和释放锁的原子性操作
     *
     * @param lockKey 锁的业务键
     * @param requestId 请求标识(必须与获取锁时使用的标识一致)
     * @return true表示释放成功,false表示释放失败(锁不存在或不属于当前请求)
     */
    public Boolean releaseLock(String lockKey, String requestId) {
        String fullLockKey = LOCK_PREFIX + lockKey;
        
        // 创建Redis脚本对象
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_SCRIPT, Long.class);
        
        // 执行Lua脚本(原子操作)
        Long result = redisTemplate.execute(redisScript, Collections.singletonList(fullLockKey), requestId);
        
        return result != null && result == 1;
    }
}

2.缓存实战:缓存穿透/击穿/雪崩解决方案(CacheService.java)

@Service
public class CacheService {

    @Autowired
    private RedisUtil redisUtil;

    private static final String CACHE_PREFIX = "cache:";
    private static final String NULL_CACHE = "NULL"; // 缓存空值的标识

    /**
     * 防缓存穿透:查询商品信息
     */
    public Product getProductById(Long productId) {
        String key = CACHE_PREFIX + "product:" + productId;
        // 1. 先从缓存中查询
        Product product = (Product) redisUtil.get(key);
        if (product != null) {
            // 如果缓存的是空值,直接返回null,防止缓存穿透
            if (NULL_CACHE.equals(product)) {
                return null;
            }
            return product;
        }

        // 2. 加锁,防止缓存击穿
        synchronized (this) {
            // 再次检查缓存,因为可能已经被其他线程填充
            product = (Product) redisUtil.get(key);
            if (product != null) {
                if (NULL_CACHE.equals(product)) {
                    return null;
                }
                return product;
            }

            // 3. 缓存中没有,则查询数据库
            product = productRepository.findById(productId).orElse(null);

            if (product == null) {
                // 数据库不存在,缓存一个空值(短时间),防止缓存穿透
                redisUtil.set(key, NULL_CACHE, 60, TimeUnit.SECONDS);
            } else {
                // 数据库存在,写入缓存,并设置随机过期时间防止雪崩
                long expireTime = 1800 + new Random().nextInt(600); // 基础30分钟 + 随机10分钟
                redisUtil.set(key, product, expireTime, TimeUnit.SECONDS);
            }
            return product;
        }
    }
}

3. 排行榜服务 (RankingService.java)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import java.util.Set;

/**
 * 排行榜服务
 * 使用Redis有序集合(ZSet)实现排行榜功能
 * 支持更新分数、获取排名、获取榜单等功能
 * 
 * @author Developer
 * @version 1.0
 */
@Service
public class RankingService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String RANKING_KEY = "leaderboard";

    /**
     * 更新玩家分数
     * 如果玩家不存在则添加,存在则更新分数
     *
     * @param playerId 玩家ID
     * @param score 玩家分数
     */
    public void updatePlayerScore(String playerId, double score) {
        redisTemplate.opsForZSet().add(RANKING_KEY, playerId, score);
    }

    /**
     * 获取前N名玩家(按分数降序)
     *
     * @param topN 前N名
     * @return 包含玩家和分数的集合
     */
    public Set<ZSetOperations.TypedTuple<Object>> getTopPlayers(int topN) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(RANKING_KEY, 0, topN - 1);
    }

    /**
     * 获取玩家排名(按分数降序,从1开始)
     *
     * @param playerId 玩家ID
     * @return 玩家排名,第1名返回1,未上榜返回null
     */
    public Long getPlayerRank(String playerId) {
        // 获取降序排名(0-based)
        Long rank = redisTemplate.opsForZSet().reverseRank(RANKING_KEY, playerId);
        return rank != null ? rank + 1 : null;
    }

    /**
     * 增加玩家分数(原子操作)
     *
     * @param playerId 玩家ID
     * @param delta 增量
     * @return 增加后的分数
     */
    public Double incrementPlayerScore(String playerId, double delta) {
        return redisTemplate.opsForZSet().incrementScore(RANKING_KEY, playerId, delta);
    }
}

三、🧪 测试控制器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.*;
import java.util.Set;
import java.util.UUID;

/**
 * Redis操作测试控制器
 * 提供HTTP接口测试各种Redis功能
 * 
 * @author Developer
 * @version 1.0
 */
@RestController
@RequestMapping("/api/redis")
public class TestController {

    @Autowired
    private StringOpsService stringOpsService;
    
    @Autowired
    private HashOpsService hashOpsService;
    
    @Autowired
    private DistributedLockService distributedLockService;
    
    @Autowired
    private RankingService rankingService;

    /**
     * 测试String操作
     *
     * @return 操作结果消息
     */
    @GetMapping("/test-string")
    public String testStringOps() {
        stringOpsService.setWithExpire("test:key", "Hello Redis!", 10, TimeUnit.MINUTES);
        return "String operation test completed.";
    }

    /**
     * 测试分布式锁功能
     *
     * @return 锁操作结果消息
     */
    @GetMapping("/test-lock")
    public String testDistributedLock() {
        String lockKey = "resource:1";
        String requestId = UUID.randomUUID().toString();
        
        boolean locked = distributedLockService.tryLock(lockKey, requestId, 10, TimeUnit.SECONDS);
        if (locked) {
            try {
                // 模拟业务操作
                Thread.sleep(2000);
                return "Lock acquired and operation performed.";
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return "Operation interrupted.";
            } finally {
                distributedLockService.releaseLock(lockKey, requestId);
            }
        } else {
            return "Failed to acquire lock.";
        }
    }

    /**
     * 更新玩家分数
     *
     * @param playerId 玩家ID
     * @param score 分数
     * @return 操作结果消息
     */
    @PostMapping("/update-score/{playerId}")
    public String updateScore(@PathVariable String playerId, @RequestParam double score) {
        rankingService.updatePlayerScore(playerId, score);
        return "Score updated for player: " + playerId;
    }

    /**
     * 获取排行榜前N名玩家
     *
     * @param n 前N名
     * @return 玩家排名集合
     */
    @GetMapping("/leaderboard/top/{n}")
    public Set<ZSetOperations.TypedTuple<Object>> getTopPlayers(@PathVariable int n) {
        return rankingService.getTopPlayers(n);
    }
}

到此这篇关于SpringBoot集成RedisTemplate的实现示例的文章就介绍到这了,更多相关SpringBoot RedisTemplate内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文