配置 RedisTemplate,并学习如何在 Spring REST 控制器中访问不同的操作捆绑包,以便从 Redis 读取和写入数据。
在本课程中,学生将学习
如果遇到困难
Spring Data Redis 为 Redis 提供了 Spring Data 平台的抽象。
我们将首先配置一个 RedisTemplate,这是一个在 Spring 和 Redis 命令之间提供线程安全桥梁的类。它负责连接管理,让开发者无需关心打开和关闭 Redis 连接。
从主应用类 Redi2readApplication
开始,该类位于 src/main/java/com/redislabs/edu/redi2read/Redi2readApplication.java
。
添加一个使用 @Bean
注解的方法,名为 redisTemplate,该方法接受一个 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;
请注意,虽然模板类型是泛型的,但正确地将给定的对象转换成二进制数据或从二进制数据转换回来取决于序列化器/反序列化器。
我们也可以通过定义一个使用 @Bean
注解的方法来编程配置 Redis 主机和端口,该方法返回一个 RedisConnectionFactory
(例如 JedisConnectionFactory
),并使用 setHostName
和 setPort
方法。
但由于 Spring Data Redis 可以使用属性文件(Java Properties 或 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
注解来标注类。控制器现在将监听以 http://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 演示的那样。
首先,我们将创建一个 String,用于作为我们写入 Redis 的键的前缀
private static final String STRING_KEY_PREFIX = "redi2read:strings:";
该方法使用 @PostMapping
注解,路径为 /strings
,这样我们 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 'http://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
实例模板的 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 String,其值为 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 请求来检索 String 键
$ curl --location --request GET 'http://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 的响应(可能只包含上面的 status 和 error 字段)。