Spring Boot 整合 Redis(基础篇)
1. 有关依赖的添加
1. 添加 Redis 依赖
1
2
3
4
|
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
|
2. 连接器依赖
Spring Data Redis在org.springframework.data.redis.connection包中提供了接口 RedisConnection 和 RedisConnectionFactory,用于连接 Redis 服务器。Spring Data Redis 提供了多种连接器,如 Jedis、Lettuce、Redis 等。
Spring 默认使用 Lettuce 作为连接器,如果想使用 Jedis 作为连接器,需要添加 Jedis 依赖。
2.1. 添加 Jedis 依赖
1
2
3
4
|
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
|
注意:Spring Boot 2.0 之后默认使用 Lettuce 作为连接器,如果想使用 Jedis 作为连接器,需要排除 Lettuce 依赖。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce.core</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
|
2.2. 添加 Lettuce 依赖
1
2
3
4
|
<dependency>
<groupId>io.lettuce.core</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
|
2. 配置 Redis
1. application.properties 配置
1
2
3
4
5
6
7
|
# application.properties
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.password=${REDIS_PASSWORD:} # 环境变量优先
spring.data.redis.database=0
spring.data.redis.lettuce.pool.max-active=8 # 连接池配置
spring.data.redis.timeout=5000 # 超时配置
|
3. RedisTemplate 与数据操作类的使用
3.1 数据操作类的使用
使用Spring 容器注入 RedisTemplate 对象,通过 RedisTemplate 对象操作 Redis 数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package cn.programcx.springbootinit.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class Config {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379));
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
|
然后在使用时,通过 RedisTemplate 对象操作 Redis 数据。
3.2 序列化器的使用
StringRedisTemplate 继承 RedisTemplate,并指定了序列化器为StringRedisSerializer以及编码集为UTF-8。这样一来,使用StringRedisTemplate对象操作 Redis 数据时,数据会以字符串形式存储,而不是以字节数组形式存储,不需要翻译。
上面创建RedisTemplate对象时,没有指定序列化器,那么Spring会使用默认的序列化器,即JdkSerializationRedisSerializer。这样一来,文本前面会出现“\xac\xed\x00\x05t\x00\”这样的字符,不方便阅读。
解决这个问题有两种方法:
- 通过编码为RedisTemplate对象指定序列化器。
示例:
1
2
|
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
|
- 注入使用SpringRedisTemplate,首先在容器中注入StringRedisTemplate对象,然后使用StringRedisTemplate对象操作 Redis 数据。
1
2
3
4
|
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory){
return new StringRedisTemplate(redisConnectionFactory);
}
|
在注入ValueOperations的位置指定stringRedisTemplate对象,然后使用stringRedisTemplate对象操作 Redis 数据:
1
2
|
@Resource(name = "stringRedisTemplate")
private ValueOperations valueOperations;
|
补充:
如果我们要定义字符串、列表、集合、散列、有序集合等数据类型的操作。
我们可以使用GenericJackson2JsonRedisSerializer
作为序列化器,这样我们可以直接存储对象。
在此之前,我们要添加依赖:
1
2
3
4
|
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
|
配置类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer()); // Hash key序列化
template.setHashValueSerializer(serializer);
return template;
}
|
3.3 RedisCallback、SessionCallback 接口和 Redis 事务的使用
RedisCallback 接口和 SessionCallback 接口是 Spring Data Redis 提供的两个回调接口,用于执行 Redis 命令。 RedisCallback 接口用于执行 Redis 命令,SessionCallback 接口用于执行 Redis 事务。
RedisCallback 接口代码如下:
1
2
3
|
public interface RedisCallback<T> {
T doInRedis(RedisConnection connection) throws DataAccessException;
}
|
比如,我们要获取Redis查询的结果,Redis查询完成后我们怎么获取结果呢,这时候就需要用到 RedisCallback 接口。
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Autowired
StringRedisTemplate stringRedisTemplate;
@Test
void testRedisCallback() {
String value = stringRedisTemplate.execute( (RedisCallback<String>)(connection) ->{
byte[] valueBytes = connection.get("redis-template".getBytes());
try {
return new String(valueBytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
});
assert value.equals("Hello, World!");
}
|
SessionCallback 主要用于对 Redis 事务的支持。
SessionCallback 接口代码如下:
1
|
<K, V> T execute(RedisOperations<K, V> operations) throws DataAccessException;
|
在此之前,我们需要开启 Redis 事务支持,在 RedisTemplate 对象中设置开启事务支持:
1
2
3
4
5
6
7
|
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setEnableTransactionSupport(true);
return redisTemplate;
}
|
写:
示例
1
2
3
4
5
6
7
8
9
10
11
12
|
@Test
void testSessionCallback() {
List<Object> results = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForValue().set("key1", "value1");
operations.opsForValue().set("key2", "value2");
return operations.exec();
}
});
}
|
读:
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Test
void readSessionCallback(){
List<Object> result = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForValue().get("key1");
operations.opsForValue().get("key2");
return operations.exec();
}
});
for(Object o: result){
System.out.println(o);
}
}
|
3.4 RedisTemplate 的使用
RedisTemplate 是 Spring Data Redis 提供的核心类,用于操作 Redis 数据。RedisTemplate 提供了多种数据操作方法,如操作字符串、列表、集合、散列、有序集合等。
RedisTemplate 提供了多种数据操作方法,如:
-
opsForValue():操作字符串类型的数据。
-
opsForList():操作列表类型的数据。
-
opsForSet():操作集合类型的数据。
-
opsForZSet():操作有序集合类型的数据。
-
opsForHash():操作散列类型的数据。
-
execute():执行 Redis 命令。
-
delete():删除 Redis 数据。
-
expire():设置 Redis 数据的过期时间。
如何使用呢?
比如,我们要插入一个列表数据,可以使用opsForList()
方法:
1
2
3
|
stringRedisTemplate.opsForList().leftPush("list", "a");
stringRedisTemplate.opsForList().leftPush("list", "b");
stringRedisTemplate.opsForList().leftPush("list", "c");
|
然后我们可以使用opsForList()
方法获取列表数据:
1
2
3
4
|
List<String> list = stringRedisTemplate.opsForList().range("list", 0, -1);
for(String s: list){
System.out.println(s);
}
|
如果我们要插入一个字符串数据,可以使用opsForValue()
方法:
1
2
|
stringRedisTemplate.opsForValue().set("key", "value");
|
然后我们可以使用opsForValue()
方法获取字符串数据:
1
2
3
4
|
String value = stringRedisTemplate.opsForValue().get("key");
System.out.println(value);
|
4. 使用 Spring 缓存注解操作 Redis
4.1 启用缓存和配置缓存管理器
Spring Boot 提供了 @EnableCaching 注解,用于启用缓存功能。在Spring Boot 程序入口处添加 @EnableCaching 注解,启用缓存功能。
1
2
3
4
5
6
7
8
|
@SpringBootApplication
@EnableAsync
@EnableCaching
public class SpringBootInitApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootInitApplication.class, args);
}
}
|
4.2 使用缓存注解开发
Spring Boot 提供了多个缓存注解,如 @Cacheable、@CachePut、@CacheEvict、@Caching 和 @CacheConfig。
- @Cacheable:缓存方法的返回结果。
- @CachePut:将方法的返回结果放入缓存。
- @CacheEvict:删除缓存。
- @Caching:组合多个缓存操作。
4.2.1 配置缓存注解序列化器
Spring Boot 默认使用 JdkSerializationRedisSerializer 作为序列化器,会导致缓存的值前面出现“\xac\xed\x00\x05t\x00\”这样的字符,不方便阅读。所以我们要指定 StringRedisSerializer 作为序列化器。
在配置类中添加以下代码:
1
2
3
4
5
6
7
8
9
|
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.disableCachingNullValues();
return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration).build();
}
|
4.2.2 缓存注解的使用
- @Cacheable:缓存方法的返回结果。
新建一个 Service类型的类,添加 @Cacheable 注解,指定缓存的名称和缓存的 key。
为什么要在 Service
类里面使用注解?
-
Service 类是由Spring Bean容器管理的,Redis 缓存注解的实现是依赖AOP
的,所以需要在由Spring Bean容器管理的类中使用。否则缓存注解不生效。
-
在 Service 类中使用缓存注解,可以将缓存的逻辑和业务逻辑分离,提高代码的可读性和可维护性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package cn.programcx.springbootinit.services;
;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class CacheServices {
@Cacheable(value = "bookname",key = "#id")
public String findBookNameById(int id) {
return "RedisCookbook";
}
}
|
然后我们在测试类中调用这个方法,第一次调用会执行方法,第二次调用会从缓存中获取结果。
1
2
3
4
5
6
7
|
@Autowired
private CacheServices cacheServices;
@Test
void testFindBookNameById(){
System.out.println(cacheServices.findBookNameById(1)); //返回 RedisCookbook
}
|
我们打开redis-cli
查看键值存放:
我们可以在结果中找到:
输入以下命令查看键值对:
1
2
|
127.0.0.1:6379> get bookname::1
"RedisCookbook"
|
- @CachePut:将方法的返回结果放入缓存。
1
2
3
4
|
@CachePut(value = "bookname",key = "#id")
public String updateBookNameById(int id) {
return "SpringBootCookbook";
}
|
- @CacheEvict:删除缓存。
1
2
3
4
|
@CacheEvict(value = "bookname",key = "#id")
public void deleteBookNameById(int id) {
System.out.println("delete bookname by id");
}
|
5. Redis 全局异常处理
5.1. Redis 异常处理
在使用 Redis 过程中,可能会出现 Redis 连接异常、Redis 命令执行异常等问题。为了保证程序的正常运行,需要对 Redis 异常进行处理。
5.2. Redis 异常处理示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@ControllerAdvice
public class RedisExceptionHandler {
@ExceptionHandler(RedisConnectionFailureException.class)
public ResponseEntity<String> handleConnectionError(RedisConnectionFailureException ex) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body("Redis服务不可用: " + ex.getMessage());
}
@ExceptionHandler(DataAccessException.class)
public ResponseEntity<String> handleDataAccessException(DataAccessException ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("数据访问异常: " + ex.getMessage());
}
}
|