Spring Boot 集成 Redis 的完整流程

Spring Boot 集成 Redis 示例

概述

本文档介绍如何在 Spring Boot 项目中集成 Redis,包括基本配置、数据操作、缓存使用和高级功能。

企业信息化平台开源社区:

gitee gitee.com/starrystar-...

github github.com/starrystar-...

环境要求

  • JDK 1.8+
  • Maven 3.6+
  • Redis 5.0+

项目创建与配置

1. 创建 Spring Boot 项目

使用 Spring Initializr 创建项目,选择以下依赖:

  • Spring Web
  • Spring Data Redis

或手动添加 Maven 依赖:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <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>
</dependencies>

2. 配置文件

application.yml 中配置 Redis:

yaml 复制代码
spring:
  redis:
    host: localhost
    port: 6379
    password: 
    database: 0
    timeout: 3000ms
    lettuce:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0

3. Redis 配置类

创建 Redis 配置类以自定义序列化方式:

java 复制代码
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> jsonSerializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), 
            ObjectMapper.DefaultTyping.NON_FINAL);
        jsonSerializer.setObjectMapper(om);
        
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

Redis 工具类

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

java 复制代码
@Component
public class RedisUtil {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // =============================common============================
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    // ============================String=============================
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }
    
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    // 更多方法:hash、list、set、zset操作...
}

使用 Spring Cache 注解

1. 启用缓存

在主应用类上添加 @EnableCaching 注解:

java 复制代码
@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2. 创建实体类

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

3. 创建 Service 层

java 复制代码
@Service
@CacheConfig(cacheNames = "user")
public class UserService {
    
    // 模拟数据库
    private static Map<Long, User> userMap = new HashMap<>();
    static {
        userMap.put(1L, new User(1L, "张三", 25, "zhangsan@example.com"));
        userMap.put(2L, new User(2L, "李四", 30, "lisi@example.com"));
        userMap.put(3L, new User(3L, "王五", 28, "wangwu@example.com"));
    }
    
    @Cacheable(key = "#id")
    public User getUserById(Long id) {
        System.out.println("查询数据库,用户ID: " + id);
        return userMap.get(id);
    }
    
    @CachePut(key = "#user.id")
    public User updateUser(User user) {
        System.out.println("更新数据库,用户: " + user);
        userMap.put(user.getId(), user);
        return user;
    }
    
    @CacheEvict(key = "#id")
    public void deleteUser(Long id) {
        System.out.println("删除数据库用户,ID: " + id);
        userMap.remove(id);
    }
    
    @Caching(evict = {
        @CacheEvict(value = "users", allEntries = true)
    })
    public void clearCache() {
        System.out.println("清除所有用户缓存");
    }
}

4. 创建 Controller

java 复制代码
@RestController
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private RedisUtil redisUtil;
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @PutMapping
    public User updateUser(@RequestBody User user) {
        return userService.updateUser(user);
    }
    
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
    
    @PostMapping("/manual")
    public boolean setManual(@RequestBody User user) {
        return redisUtil.set("user:manual:" + user.getId(), user, 300);
    }
    
    @GetMapping("/manual/{id}")
    public Object getManual(@PathVariable Long id) {
        return redisUtil.get("user:manual:" + id);
    }
}

Redis 高级功能

1. 发布订阅模式

java 复制代码
// 配置Redis消息监听容器
@Configuration
public class RedisPubSubConfig {
    
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                                   MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new ChannelTopic("chat"));
        return container;
    }
    
    @Bean
    public MessageListenerAdapter listenerAdapter(RedisReceiver receiver) {
        return new MessageListenerAdapter(receiver, "receiveMessage");
    }
}

// 消息接收器
@Component
public class RedisReceiver {
    public void receiveMessage(String message) {
        System.out.println("收到消息: " + message);
    }
}

// 消息发布器
@Component
public class RedisPublisher {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void publish(String channel, String message) {
        redisTemplate.convertAndSend(channel, message);
    }
}

2. 分布式锁

java 复制代码
@Component
public class RedisDistributedLock {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String LOCK_PREFIX = "lock:";
    private static final long DEFAULT_EXPIRE = 30000; // 30秒
    
    public boolean tryLock(String key, String value, long expire) {
        if (expire <= 0) {
            expire = DEFAULT_EXPIRE;
        }
        
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(LOCK_PREFIX + key, value, expire, TimeUnit.MILLISECONDS);
        return Boolean.TRUE.equals(result);
    }
    
    public boolean releaseLock(String key, String value) {
        String lockKey = LOCK_PREFIX + key;
        String currentValue = (String) redisTemplate.opsForValue().get(lockKey);
        
        if (currentValue != null && currentValue.equals(value)) {
            redisTemplate.delete(lockKey);
            return true;
        }
        return false;
    }
}

测试

1. 单元测试

java 复制代码
@SpringBootTest
class RedisApplicationTests {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Test
    void testCache() {
        // 第一次查询,会访问数据库
        User user1 = userService.getUserById(1L);
        assertNotNull(user1);
        
        // 第二次查询,应该从缓存获取
        User user2 = userService.getUserById(1L);
        assertNotNull(user2);
        
        // 验证两次返回的是同一个对象
        assertEquals(user1, user2);
    }
    
    @Test
    void testRedisTemplate() {
        redisTemplate.opsForValue().set("testKey", "testValue");
        String value = (String) redisTemplate.opsForValue().get("testKey");
        assertEquals("testValue", value);
    }
}

2. API 测试

使用 curl 或 Postman 测试 API:

shell 复制代码
# 获取用户
curl http://localhost:8080/users/1

# 更新用户
curl -X PUT http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{"id":1,"name":"张三更新","age":26,"email":"zhangsan_updated@example.com"}'

# 手动设置缓存
curl -X POST http://localhost:8080/users/manual \
  -H "Content-Type: application/json" \
  -d '{"id":100,"name":"测试用户","age":30,"email":"test@example.com"}'

常见问题与解决方案

1. 连接超时

检查 Redis 服务器是否正常运行,网络是否通畅,防火墙设置是否正确。

2. 序列化错误

确保所有存储在 Redis 中的对象都实现了 Serializable 接口,或使用 JSON 序列化。

3. 缓存穿透

使用空值缓存或布隆过滤器防止缓存穿透:

java 复制代码
@Cacheable(key = "#id", unless = "#result == null")
public User getUserById(Long id) {
    User user = userRepository.findById(id);
    if (user == null) {
        // 缓存空值,防止穿透
        redisTemplate.opsForValue().set("user:null:" + id, "", 5, TimeUnit.MINUTES);
    }
    return user;
}

4. 缓存雪崩

设置不同的过期时间防止缓存雪崩:

java 复制代码
@Cacheable(key = "#id", unless = "#result == null")
@CacheExpire(value = 300 + new Random().nextInt(100)) // 随机过期时间
public User getUserById(Long id) {
    return userRepository.findById(id);
}

性能优化建议

  1. 使用连接池减少连接创建开销
  2. 合理设置缓存过期时间
  3. 使用 Pipeline 减少网络往返次数
  4. 对大对象进行压缩存储
  5. 使用本地缓存+Redis的多级缓存架构

总结

本文档详细介绍了 Spring Boot 集成 Redis 的完整流程,包括基本配置、数据操作、缓存使用和高级功能。通过合理使用 Redis,可以显著提升应用程序的性能和可扩展性。

参考资料

  1. Spring Data Redis 官方文档
  2. Redis 官方文档
  3. Spring Boot 缓存指南
相关推荐
昨日的风3 小时前
springboot 多数据源切换
后端
绝无仅有3 小时前
mysql性能优化实战与总结
后端·面试·github
用户8356290780513 小时前
从手动编辑到代码生成:Python 助你高效创建 Word 文档
后端·python
德育处主任3 小时前
玩转 Strands:AI Agent 开发,原来可以这么简单!
后端·aigc
Undoom3 小时前
大模型选型“炼狱”与终结:一份来自普通开发者的AI Ping深度评测报告
后端
用户4099322502123 小时前
FastAPI的CI流水线怎么自动测端点,还能让Allure报告美到犯规?
后端·ai编程·trae
双向333 小时前
Docker 镜像瘦身实战:从 1.2GB 压缩到 200MB 的优化过程
后端
Cyan_RA93 小时前
计算机网络面试题 — TCP连接如何确保可靠性?
前端·后端·面试
BingoGo4 小时前
PHP-FPM 深度调优指南 告别 502 错误,让你的 PHP 应用飞起来
后端·php