学习

Redis 数据 Spring 简介

Brian Sam-Bodden
作者
Brian Sam-Bodden, Redis 开发倡导者

目标#

配置 RedisTemplate 并了解如何访问不同的操作包以在 Spring REST 控制器中读写 Redis 数据。

议程#

在本课程中,学生将学习

  • 如何从应用程序配置与 Redis 的连接
  • 如何访问和配置 Spring Data RedisTemplate
  • 如何使用 opsForXXX 读取和写入 Redis 数据
  • Spring 的 @RestController 基础

如果你遇到问题

介绍 Spring Data Redis#

Spring Data Redis 为 Redis 提供了 Spring Data 平台的抽象。

  • Spring Data 简化了数据访问技术的应用,包括关系型和非关系型数据库、map-reduce 框架以及云数据服务。要了解有关 Spring Data 的更多信息,请参阅 https://springframework.org.cn/projects/spring-data.
  • Spring Data Redis 提供了从 Spring 应用程序访问 Redis 的能力。它为与 Redis 交互提供了低级和高级抽象。

Redis 模板#

我们将首先配置一个 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 注释方法,并使用 setHostNamesetPort 方法以编程方式配置 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

使用 Redis 模板#

为了测试 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;

接下来,让我们注入一个 @AutowiredRedisTemplate 实例。请注意,我们将使用具体的类作为 RedisTemplate<K,V> 中的 KV 参数类。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 的响应(可能只是上面的状态和错误字段)。