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);
}
性能优化建议
- 使用连接池减少连接创建开销
- 合理设置缓存过期时间
- 使用 Pipeline 减少网络往返次数
- 对大对象进行压缩存储
- 使用本地缓存+Redis的多级缓存架构
总结
本文档详细介绍了 Spring Boot 集成 Redis 的完整流程,包括基本配置、数据操作、缓存使用和高级功能。通过合理使用 Redis,可以显著提升应用程序的性能和可扩展性。