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 缓存指南
相关推荐
Captaincc6 分钟前
AI 能帮你写代码,但把代码变成软件,还是得靠人
前端·后端·程序员
Rocket MAN15 分钟前
Spring Boot 缓存:工具选型、两级缓存策略、注解实现与进阶优化
spring boot·后端·缓存
Tony Bai16 分钟前
【Go 网络编程全解】14 QUIC 与 HTTP/3:探索下一代互联网协议
开发语言·网络·后端·http·golang
紫荆鱼2 小时前
设计模式-状态模式(State)
c++·后端·设计模式·状态模式
A接拉起0072 小时前
如何丝滑迁移 Mongodb 数据库
后端·mongodb·架构
qincloudshaw2 小时前
Linux系统下安装JDK并设置环境变量
后端
程序定小飞2 小时前
基于springboot的民宿在线预定平台开发与设计
java·开发语言·spring boot·后端·spring
代码扳手3 小时前
Golang 实战:用 Watermill 构建订单事件流系统,一文掌握概念与应用
后端·go
麻木森林3 小时前
利用Apipost 的AI能力轻松破解接口测试的效率与质量困局
后端·api
紫荆鱼4 小时前
设计模式-代理模式(Proxy)
c++·后端·设计模式·代理模式