配置 RedisTemplate 并了解如何访问不同的操作包以在 Spring REST 控制器中读写 Redis 数据。
在本课程中,学生将学习
如果你遇到问题
Spring Data Redis 为 Redis 提供了 Spring Data 平台的抽象。
我们将首先配置一个 RedisTemplate,它是一个提供 Spring 和 Redis 命令之间线程安全桥梁的类。它处理连接管理,从而使开发人员无需打开和关闭 Redis 连接。
从主应用程序类 Redi2readApplication
开始,它位于 src/main/java/com/redislabs/edu/redi2read/Redi2readApplication.java
中。
添加一个名为 redisTemplate 的 @Bean
注释方法,该方法接受一个 RedisConnectionFactory
并返回一个 RedisTemplate
。该方法将在 Spring 容器中提供一个配置后的 RedisTemplate
类型的 bean。我们可以在需要访问 Redis 的任何地方注入此 bean。
@Bean
public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<?, ?> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
这需要以下导入
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
请注意,虽然模板类型是通用的,但序列化器/反序列化器负责正确地将给定对象转换为二进制数据,以及从二进制数据转换回来。
我们还可以通过定义一个返回 RedisConnectionFactory
(可以是 JedisConnectionFactory
)的 @Bean
注释方法,并使用 setHostName
和 setPort
方法以编程方式配置 Redis 主机和端口。
但由于 Spring Data Redis 可以使用属性文件(可以是 Java 属性或 YAML)配置 bean,因此我们将使用 applications.properties
文件。
Spring Data Redis 属性以 spring.redis.
为前缀。在 src/main/resources/application.properties
文件中添加以下属性
spring.redis.host=localhost
spring.redis.port=6379
趁此机会,我们也排除 Spring Security 的自动配置。由于我们已经包含了 Spring Security 的 Maven 依赖项,但尚未配置它,因此 Spring 默认情况下会谨慎行事,并保护应用程序上的所有端点。因此,现在我们将禁用安全自动配置,直到我们决定保护我们的服务。
spring.redis.host=localhost
spring.redis.port=6379
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
为了测试 RedisTemplate,我们将创建一个 REST 控制器,并使用它对我们的 Redis 实例执行一些操作。
我们将把控制器添加到 src/main/java/com/redislabs/edu/redi2read/controllers 文件夹中,这意味着它将位于 com.redislabs.edu.redi2read.controllers 包中。
package com.redislabs.edu.redi2read.controllers;
public class HelloRedisController {
}
接下来,让我们使用 @RestController
和 @RequestMapping
注释对类进行注释。控制器现在将监听根路径为 https://localhost:8080/api/redis 的请求。
@RestController
@RequestMapping("/api/redis")
public class HelloRedisController {
}
添加必要的导入,如下所示
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
接下来,让我们注入一个 @Autowired
的 RedisTemplate
实例。请注意,我们将使用具体的类作为 RedisTemplate<K,V>
中的 K
和 V
参数类。K
是 Redis 键类型(通常是 String
),V
是 Redis 值类型(即映射到 Redis 数据结构的内容)。
@RestController
@RequestMapping("/api/redis")
public class HelloRedisController {
@Autowired
private RedisTemplate<String, String> template;
}
添加必要的导入,如下所示
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
现在,我们只需要一个控制器方法来运行一些 Redis 命令。我们将使用 Redis SET 命令(https://redis.ac.cn/commands/set),正如我们之前使用 Redis CLI 所示。
首先,我们将创建一个字符串,用它来作为我们写入 Redis 的键的前缀
private static final String STRING_KEY_PREFIX = "redi2read:strings:";
该方法使用路径为 /strings
的 @PostMapping
进行注释,这使得我们的 post 的有效路径为 /api/redis/strings
。@Request
主体和方法的返回值是一个 Map.Entry<String, String>
,这在处理键值对时很方便。
@PostMapping("/strings")
@ResponseStatus(HttpStatus.CREATED)
public Map.Entry<String, String> setString(@RequestBody Map.Entry<String, String> kvp) {
return kvp;
}
添加必要的导入,如下所示
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
如果我们现在使用以下命令启动应用程序
./mvnw clean spring-boot:run
我们可以使用 curl 来调用我们的 api/redis/strings
端点
$ curl --location --request POST 'https://localhost:8080/api/redis/strings' \
--header 'Content-Type: application/json' \
--data-raw '{ "database:redis:creator": "Salvatore Sanfilippo" }'
{"database:redis:creator":"Salvatore Sanfilippo"}
这会导致 JSON 有效负载被回显回来。让我们完成 setString 方法的实现,以便我们可以将 Redis 字符串写入数据库
@PostMapping("/strings")
@ResponseStatus(HttpStatus.CREATED)
public Map.Entry<String, String> setString(@RequestBody Map.Entry<String, String> kvp) {
template.opsForValue().set(STRING_KEY_PREFIX + kvp.getKey(), kvp.getValue());
return kvp;
}
我们将使用 RedisTemplate
实例 template opsForValue()
方法来获取 ValueOperations
的实例,它提供了执行对简单值(或 Redis 术语中的 Strings
)执行的操作的方法。Redis SET 方法是使用(你猜对了!)set()
方法实现的,该方法接受一个键名和一个值。我们在提供的键名之前添加了 KEY_SPACE_PREFIX。在发出另一个 curl 请求之前,让我们使用 MONITOR 标记启动一个 Redis CLI 实例,以便我们可以在访问服务器时观察发生的情况。
$ redis-cli MONITOR
现在,当你再次发出 POST 请求时,你应该会看到类似以下的输出
1617346602.221390 [0 172.19.0.1:58396] "SET" "redi2read:strings:database:redis:creator" "Salvatore Sanfilippo"
我们可以启动另一个 Redis CLI 来自己查询 Redis
127.0.0.1:6379> KEYS *
1) "redi2read:strings:database:redis:creator"
127.0.0.1:6379> TYPE "redi2read:strings:database:redis:creator"
string
127.0.0.1:6379> GET "redi2read:strings:database:redis:creator"
"Salvatore Sanfilippo"
127.0.0.1:6379>
如果我们使用 KEYS * 命令,我们可以看到存储在 Redis 中的所有键(不要在拥有大量数据的生产环境中这样做,因为你会在服务大量响应时阻塞你的 Redis 实例)。redi2read:strings:database:redis:creator
键已被创建,它是一个 Redis 字符串,其值为 Salvatore Sanfilipo
现在我们可以通过 REST 控制器将字符串写入 Redis。接下来,让我们在控制器中添加一个相应的 GET 方法来读取字符串值。
@GetMapping("/strings/{key}")
public Map.Entry<String, String> getString(@PathVariable("key") String key) {
String value = template.opsForValue().get(STRING_KEY_PREFIX + key);
if (value == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "key not found");
}
return new SimpleEntry<String, String>(key, value);
}
使用导入
import java.util.AbstractMap.SimpleEntry;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.server.ResponseStatusException;
我们现在可以发出 GET 请求来检索字符串键
$ curl --location --request GET 'https://localhost:8080/api/redis/strings/database:redis:creator'
{"database:redis:creator":"Salvatore Sanfilippo"}
在 Redis CLI 监控器上,你应该看到
1617347871.901585 [0 172.19.0.1:58284] "GET" "redi2read:strings:database:redis:creator"
请注意,为了在键未找到时返回错误,我们必须检查结果是否为 null 并抛出适当的异常。
{
"timestamp": "2021-04-02T07:45:10.303+00:00",
"status": 404,
"error": "Not Found",
"trace": "org.springframework.web.server.ResponseStatusException: 404...\n",
"message": "key not found",
"path": "/api/redis/strings/database:neo4j:creator"
}
请记住,这是一个“开发时间”异常,适合在用于开发人员的错误页面上显示。我们可能会拦截此异常并创建一个适合 API 的响应(可能只是上面的状态和错误字段)。