Spring Boot Redis 整合使用样例

97 阅读5分钟

Spring Boot Redis 整合使用样例

Spring Boot 提供了与 Redis 无缝集成的支持,可以通过简单的配置实现缓存管理、数据存储等功能。以下是完整的 Spring Boot Redis 整合教程和使用样例:

一、基础配置

1. 添加依赖

pom.xml 文件中添加 Redis 相关依赖:

<dependencies>
    <!-- Spring Boot Redis 支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- 可选:连接池支持 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    
    <!-- Spring Boot Web 支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

2. 配置文件

application.yml 中配置 Redis 连接信息:

spring:
  redis:
    host: localhost  # Redis 服务器地址
    port: 6379       # Redis 端口
    database: 0      # Redis 数据库索引
    password:        # Redis 密码(如果有)
    timeout: 10000   # 连接超时时间(毫秒)
    lettuce:
      pool:
        max-active: 8    # 最大连接数
        max-wait: -1     # 最大阻塞等待时间(-1表示无限制)
        max-idle: 8      # 最大空闲连接
        min-idle: 0      # 最小空闲连接

二、自定义 Redis 配置类

创建 Redis 配置类,自定义序列化方式和缓存管理器:

package com.example.redis.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching  // 开启缓存支持
public class RedisConfig {

    /**
     * 自定义 RedisTemplate,设置序列化器
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 设置键的序列化器
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        
        // 设置值的序列化器
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
    
    /**
     * 自定义缓存管理器
     */
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))  // 默认缓存过期时间1小时
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();  // 禁用缓存空值
        
        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
    }
    
    /**
     * 自定义键生成器
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(":");
            sb.append(method.getName());
            if (params != null && params.length > 0) {
                sb.append(":");
                for (int i = 0; i < params.length; i++) {
                    sb.append(params[i]);
                    if (i < params.length - 1) {
                        sb.append(",");
                    }
                }
            }
            return sb.toString();
        };
    }
}

三、Redis 操作工具类

创建一个 Redis 操作工具类,封装常用操作:

package com.example.redis.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Collection;
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 Boolean delete(String key) {
        return redisTemplate.delete(key);
    }
    
    /**
     * 批量删除
     */
    public Long delete(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }
    
    /**
     * 设置过期时间
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }
    
    // ============================== Hash操作 ==============================
    
    /**
     * 存储Hash值
     */
    public void hset(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    
    /**
     * 获取Hash值
     */
    public Object hget(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    
    /**
     * 获取Hash所有字段
     */
    public Map<Object, Object> hgetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    
    /**
     * 删除Hash字段
     */
    public Long hdel(String key, Object... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }
    
    // ============================== List操作 ==============================
    
    /**
     * 左侧插入List
     */
    public Long lpush(String key, Object... values) {
        return redisTemplate.opsForList().leftPushAll(key, values);
    }
    
    /**
     * 右侧插入List
     */
    public Long rpush(String key, Object... values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }
    
    /**
     * 获取List范围
     */
    public List<Object> lrange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    
    // ============================== Set操作 ==============================
    
    /**
     * 添加元素到Set
     */
    public Long sadd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }
    
    /**
     * 获取Set所有元素
     */
    public Set<Object> smembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    
    /**
     * 判断元素是否在Set中
     */
    public Boolean sismember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }
    
    // ============================== ZSet操作 ==============================
    
    /**
     * 添加元素到有序集合
     */
    public Boolean zadd(String key, Object value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }
    
    /**
     * 获取有序集合范围
     */
    public Set<Object> zrange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }
}

四、缓存注解使用样例

1. 实体类

package com.example.redis.entity;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String username;
    private String email;
    private Date createTime;
    
    // 构造函数、getter和setter方法
    // ...
}

2. Service 层使用缓存注解

package com.example.redis.service;

import com.example.redis.entity.User;
import com.example.redis.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
@CacheConfig(cacheNames = "userCache")  // 类级别缓存配置
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 查询用户,使用缓存
     */
    @Cacheable(key = "#id")  // 缓存键为id
    public User getUserById(Long id) {
        // 缓存未命中时执行此方法
        System.out.println("从数据库查询用户: " + id);
        return userMapper.selectById(id);
    }
    
    /**
     * 条件缓存
     */
    @Cacheable(key = "#username", condition = "#username.length() > 3")
    public User getUserByUsername(String username) {
        return userMapper.selectByUsername(username);
    }
    
    /**
     * 更新用户,更新缓存
     */
    @CachePut(key = "#user.id")
    public User updateUser(User user) {
        userMapper.updateById(user);
        return user;
    }
    
    /**
     * 删除用户,清除缓存
     */
    @CacheEvict(key = "#id")
    public void deleteUser(Long id) {
        userMapper.deleteById(id);
    }
    
    /**
     * 清除所有缓存
     */
    @CacheEvict(allEntries = true)
    public void clearCache() {
        // 此方法仅用于清除缓存
        System.out.println("清除所有用户缓存");
    }
    
    /**
     * 组合缓存操作
     */
    @Caching(
        cacheable = { @Cacheable(key = "#user.id") },
        evict = { @CacheEvict(key = "#user.username") }
    )
    public User complexCacheOperation(User user) {
        // 复杂的缓存操作
        return user;
    }
}

五、RedisTemplate 直接使用样例

1. Controller 层示例

package com.example.redis.controller;

import com.example.redis.entity.User;
import com.example.redis.service.UserService;
import com.example.redis.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private RedisUtils redisUtils;
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        // 使用RedisTemplate操作
        redisTemplate.opsForValue().set("user:" + user.getId(), user, 1, TimeUnit.HOURS);
        return user;
    }
    
    @PutMapping
    public User updateUser(@RequestBody User user) {
        return userService.updateUser(user);
    }
    
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
    
    // 使用工具类的示例
    @GetMapping("/cache/test")
    public String testCache() {
        // 字符串操作
        redisUtils.set("test:key", "test value", 10, TimeUnit.MINUTES);
        
        // Hash操作
        redisUtils.hset("user:1001", "name", "张三");
        redisUtils.hset("user:1001", "age", 28);
        
        // List操作
        redisUtils.lpush("news:list", "新闻1", "新闻2", "新闻3");
        
        // Set操作
        redisUtils.sadd("tags:set", "技术", "Java", "Spring");
        
        // ZSet操作
        redisUtils.zadd("score:rank", "user1", 95);
        redisUtils.zadd("score:rank", "user2", 88);
        
        return "Redis操作测试成功";
    }
}

六、高级应用场景

1. 分布式锁实现

package com.example.redis.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class DistributedLockService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 获取分布式锁
     */
    public boolean acquireLock(String lockKey, String requestId, long expireTime) {
        return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS);
    }
    
    /**
     * 释放分布式锁
     */
    public boolean releaseLock(String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            Object result = connection.eval(
                script.getBytes(),
                ReturnType.INTEGER,
                1,
                lockKey.getBytes(),
                requestId.getBytes()
            );
            return Integer.valueOf(1).equals(result);
        });
    }
}

2. 缓存预热

package com.example.redis.service;

import com.example.redis.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class CacheWarmUp implements ApplicationRunner {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private UserService userService;
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 应用启动时预热热点数据
        List<User> hotUsers = userService.getHotUsers();
        for (User user : hotUsers) {
            redisTemplate.opsForValue().set("user:" + user.getId(), user, 1, TimeUnit.HOURS);
        }
        System.out.println("缓存预热完成,加载了 " + hotUsers.size() + " 个热点用户");
    }
}

七、总结

Spring Boot 整合 Redis 主要有以下几种使用方式:

  1. 缓存注解方式:使用 @Cacheable@CachePut@CacheEvict 等注解,简单易用
  2. RedisTemplate 直接操作:灵活控制 Redis 的各种数据结构操作
  3. 工具类封装:将常用操作封装成工具类,提高代码复用性

在实际项目中,建议根据具体需求选择合适的方式。对于简单的缓存场景,使用注解方式最为便捷;对于复杂的 Redis 操作,则建议使用 RedisTemplate 或封装工具类。

通过合理使用 Redis,可以显著提高应用的性能,减轻数据库压力,特别是在高并发场景下效果明显。