从七年开发视角带你吃透 Spring Boot 整合 redis (附全流程实战加工具类)

292 阅读5分钟

从七年开发视角带你吃透 Spring Boot 整合 redis (附全流程实战加工具类)

干了七年开发,经手的项目大大小小几十个,Redis 几乎成了我每个项目里的 “标配”。这玩意儿就像开发工具箱里的瑞士军刀,缓存数据、实现分布式锁、做计数器,样样精通。今天就手把手教你如何用 Spring Boot 把 Redis 整合得明明白白,还附上超详细的工具类代码和注释,让你直接上手能用。

一、环境准备

首先确保你的开发环境里有 JDK(建议 1.8 及以上)、Maven 或 Gradle。创建一个 Spring Boot 项目,这里我用 Maven 来管理依赖。在 pom.xml 文件中添加 Spring Boot 整合 Redis 的依赖:

<dependencies>
    <!-- Spring Boot Web 依赖,方便后续测试接口 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Boot Redis 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>

有了这两个依赖,基本的开发环境就搭建好了。

二、配置 Redis 连接

在 application.yml 文件中配置 Redis 的连接信息,就像给你的程序指明要连到哪台 Redis 服务器:

spring:
  redis:
    host: localhost # Redis 服务器地址,根据实际情况修改
    port: 6379 # Redis 服务器端口
    password: # 如果 Redis 设置了密码,在这里填写
    database: 0 # 使用的 Redis 数据库编号,默认为 0
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数
        max-idle: 8 # 连接池最大空闲连接数
        min-idle: 0 # 连接池最小空闲连接数
        max-wait: -1ms # 获取连接最大等待时间,-1 表示无限制

这些配置就像给 Redis 连接上了 “导航”,让程序知道怎么找到它。

三、编写 Redis 工具类

接下来就是重头戏 —— 编写 Redis 工具类。这个工具类就像你和 Redis 对话的 “翻译官”,封装了常用的操作方法。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // 设置缓存
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    // 设置缓存并指定过期时间
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }
    // 获取缓存
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    // 删除缓存
    public void delete(String key) {
        redisTemplate.delete(key);
    }
    // 判断缓存是否存在
    public boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }
    // 设置哈希表字段和值
    public void hset(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    // 获取哈希表字段的值
    public Object hget(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    // 获取哈希表所有字段和值
    public Map<Object, Object> hgetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    // 删除哈希表字段
    public void hdel(String key, Object... hashKeys) {
        redisTemplate.opsForHash().delete(key, hashKeys);
    }
    // 向列表右侧添加元素
    public void rpush(String key, Object... values) {
        redisTemplate.opsForList().rightPushAll(key, values);
    }
    // 获取列表指定范围的元素
    public List<Object> lrange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    // 向集合中添加元素
    public void sadd(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }
    // 获取集合所有元素
    public Set<Object> smembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    // 对键进行自增操作
    public Long incr(String key) {
        return redisTemplate.opsForValue().increment(key);
    }
    // 对键进行自减操作
    public Long decr(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }
}

工具类里把字符串、哈希、列表、集合等常用数据结构的操作方法都封装好了,后续使用直接调用就行。

四、实战测试

在 controller 层编写接口来测试我们的 Redis 工具类,比如编写一个简单的缓存测试接口:

import com.example.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RedisTestController {
    @Autowired
    private RedisUtils redisUtils;
    @GetMapping("/setCache")
    public String setCache() {
        redisUtils.set("testKey", "testValue");
        return "缓存设置成功";
    }
    @GetMapping("/getCache")
    public Object getCache() {
        return redisUtils.get("testKey");
    }
}

启动 Spring Boot 项目,访问 http://localhost:8080/setCache 设置缓存,再访问 http://localhost:8080/getCache 获取缓存,就能看到效果了。

五、工具类的应用场景代码

1. 缓存热点数据场景

比如在一个电商系统中,商品的详情页数据访问量很大,我们可以把商品详情缓存起来,减轻数据库压力。

import com.example.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
    @Autowired
    private RedisUtils redisUtils;
    @GetMapping("/product/{productId}")
    public Object getProductDetails(@PathVariable String productId) {
        // 先从 Redis 中获取缓存数据
        Object cachedData = redisUtils.get("product:" + productId);
        if (cachedData != null) {
            return cachedData;
        }
        // 如果 Redis 中没有,从数据库查询
        Object productDetails = getProductDetailsFromDB(productId);
        // 将查询结果存入 Redis 缓存,设置过期时间为 1 小时
        redisUtils.set("product:" + productId, productDetails, 1, TimeUnit.HOURS);
        return productDetails;
    }
    private Object getProductDetailsFromDB(String productId) {
        // 这里模拟从数据库查询商品详情的逻辑
        return "商品详情数据";
    }
}

2. 分布式锁场景

在分布式系统中,多个服务实例同时访问共享资源时,需要使用分布式锁避免数据不一致。下面是一个简单的基于 Redis 的分布式锁示例:

import com.example.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DistributedLockController {
    @Autowired
    private RedisUtils redisUtils;
    private static final String LOCK_KEY = "distributed_lock";
    private static final long LOCK_EXPIRE_TIME = 10L; // 锁过期时间,单位秒
    @GetMapping("/doSomething")
    public String doSomething() {
        String lockValue = System.currentTimeMillis() + "";
        boolean lockAcquired = redisUtils.setIfAbsent(LOCK_KEY, lockValue, LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
        if (lockAcquired) {
            try {
                // 执行业务逻辑
                System.out.println("获取到锁,执行业务操作");
                return "操作成功";
            } finally {
                // 释放锁
                if (lockValue.equals(redisUtils.get(LOCK_KEY))) {
                    redisUtils.delete(LOCK_KEY);
                }
            }
        } else {
            return "锁被占用,稍后重试";
        }
    }
}

在 RedisUtils 工具类中补充 setIfAbsent 方法:

public boolean setIfAbsent(String key, Object value, long timeout, TimeUnit unit) {
    return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
}

3. 计数器场景

在统计网站的访问量、用户的点赞数等场景中,Redis 的原子自增操作非常实用。

import com.example.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CounterController {
    @Autowired
    private RedisUtils redisUtils;
    private static final String PAGE_VIEW_COUNT_KEY = "page_view_count";
    @GetMapping("/incrementPageView")
    public Long incrementPageView() {
        return redisUtils.incr(PAGE_VIEW_COUNT_KEY);
    }
    @GetMapping("/getPageViewCount")
    public Long getPageViewCount() {
        return (Long) redisUtils.get(PAGE_VIEW_COUNT_KEY);
    }
}

通过这些应用场景代码,相信你能更清楚地知道 Redis 工具类在实际项目中怎么用了。开发这条路,就是在不断实践和踩坑中成长,希望这些经验和代码能帮你少走些弯路!