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 缓存指南
相关推荐
Terio_my13 分钟前
Spring Boot 热部署配置与自定义排除项
java·spring boot·后端
大鱼七成饱23 分钟前
Rust的Option碰到指针Box:数据怎么解
后端
倔强的石头_24 分钟前
【征文计划】Rokid 语音唤醒技术深度解析:从声学模型到低功耗优化实践
后端
吾疾唯君医43 分钟前
记录GoLang创建文件并写入文件的中文乱码错误!
开发语言·后端·golang
LucianaiB1 小时前
【征文计划】用 Rokid AR 眼镜做“厨房小助手”:让做饭不再手忙脚乱
后端
数据知道1 小时前
Go基础:Go语言ORM框架GORM详解
开发语言·jvm·后端·golang·go语言
Pr Young3 小时前
go build命令
后端·golang
数据知道3 小时前
Go语言:Go 语言中的命令行参数操作详解
开发语言·后端·golang·go语言
代码匠心3 小时前
从零开始学Flink:实时流处理实战
java·大数据·后端·flink