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 缓存指南
相关推荐
点光8 小时前
使用Sentinel作为Spring Boot应用限流组件
后端
不要秃头啊8 小时前
别再谈提效了:AI 时代的开发范式本质变了
前端·后端·程序员
有志9 小时前
Java 项目添加慢 SQL 查询工具实践
后端
山佳的山9 小时前
KingbaseES 共享锁(SHARE)与排他锁(EXCLUSIVE)详解及测试复现
后端
Leo8999 小时前
rust 从零单排 之 一战到底
后端
程序员清风10 小时前
程序员兼职必看:靠谱软件外包平台挑选指南与避坑清单!
java·后端·面试
鱼人10 小时前
MySQL 实战入门:从“增删改查”到“高效查询”的核心指南
后端
大鹏198810 小时前
告别 Session:Spring Boot 实现 JWT 无状态登录认证全攻略
后端
Java编程爱好者10 小时前
从 AQS 到 ReentrantLock:搞懂同步队列与条件队列,这一篇就够了
后端
鱼人10 小时前
Nginx 全能指南:从反向代理到负载均衡,一篇打通任督二脉
后端