通过 RedisTemplate
操作对象
RedisTemplate
大多数用户可能会使用 RedisTemplate 及其对应的包 org.springframework.data.redis.core
,或者其响应式变体 ReactiveRedisTemplate。实际上,模板是 Redis 模块的核心类,因为它提供了丰富的功能集。该模板为 Redis 交互提供了高级抽象。虽然 [Reactive]RedisConnection
提供了接受和返回二进制值(byte
数组)的低级方法,但模板负责序列化和连接管理,使用户无需处理这些细节。
RedisTemplate 类实现了 RedisOperations 接口,而其响应式变体 ReactiveRedisTemplate 则实现了 ReactiveRedisOperations 接口。
在 [Reactive]RedisTemplate
实例上引用操作的首选方式是通过 [Reactive]RedisOperations
接口。
此外,该模板提供了操作视图(遵循 Redis 命令 参考 的分组),为针对特定类型或特定键的操作提供了丰富的通用化接口(通过 KeyBound
接口),如下表所述:
操作视图
- 命令式
- 响应式
接口 | 描述 |
---|---|
键类型操作 | |
GeoOperations | Redis 地理空间操作,例如 GEOADD , GEORADIUS ,… |
HashOperations | Redis 哈希操作 |
HyperLogLogOperations | Redis HyperLogLog 操作,例如 PFADD , PFCOUNT ,… |
ListOperations | Redis 列表操作 |
SetOperations | Redis 集合操作 |
ValueOperations | Redis 字符串(或值)操作 |
ZSetOperations | Redis 有序集合(或 zset)操作 |
键绑定操作 | |
BoundGeoOperations | Redis 键绑定地理空间操作 |
BoundHashOperations | Redis 哈希键绑定操作 |
BoundKeyOperations | Redis 键绑定操作 |
BoundListOperations | Redis 列表键绑定操作 |
BoundSetOperations | Redis 集合键绑定操作 |
BoundValueOperations | Redis 字符串(或值)键绑定操作 |
BoundZSetOperations | Redis 有序集合(或 zset)键绑定操作 |
接口 | 描述 |
---|---|
键类型操作 | |
ReactiveGeoOperations | Redis 地理空间操作,例如 GEOADD , GEORADIUS , 以及其他) |
ReactiveHashOperations | Redis 哈希操作 |
ReactiveHyperLogLogOperations | Redis HyperLogLog 操作,例如 (PFADD , PFCOUNT , 以及其他) |
ReactiveListOperations | Redis 列表操作 |
ReactiveSetOperations | Redis 集合操作 |
ReactiveValueOperations | Redis 字符串(或值)操作 |
ReactiveZSetOperations | Redis 有序集合(或 zset)操作 |
一旦配置完成,模板是线程安全的,并且可以在多个实例中重复使用。
RedisTemplate
在大多数操作中使用基于 Java 的序列化器。这意味着通过模板写入或读取的任何对象都会通过 Java 进行序列化和反序列化。
你可以在模板上更改序列化机制,Redis 模块提供了多种实现,这些实现位于 org.springframework.data.redis.serializer
包中。有关更多信息,请参阅序列化器。你还可以将任何一个序列化器设置为 null
,并通过将 enableDefaultSerializer
属性设置为 false
来使用原始字节数组的 RedisTemplate。请注意,模板要求所有键都不能为 null
。然而,只要底层的序列化器允许,值可以为 null
。有关更多信息,请阅读每个序列化器的 Javadoc。
对于需要特定模板视图的情况,可以将视图声明为依赖项并注入模板。容器会自动执行转换,从而消除 opsFor[X]
调用,如下例所示:
- Java Imperative
- Java Reactive
- XML
@Configuration
class MyConfig {
@Bean
LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
}
@Configuration
class MyConfig {
@Bean
LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
ReactiveRedisTemplate<String, String> ReactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) {
return new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext.string());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="redisConnectionFactory"/>
...
</beans>
- Imperative
- Reactive
public class Example {
// inject the actual operations
@Autowired
private RedisOperations<String, String> operations;
// inject the template as ListOperations
@Resource(name="redisTemplate")
private ListOperations<String, String> listOps;
public void addLink(String userId, URL url) {
listOps.leftPush(userId, url.toExternalForm());
}
}
public class Example {
// inject the actual template
@Autowired
private ReactiveRedisOperations<String, String> operations;
public Mono<Long> addLink(String userId, URL url) {
return operations.opsForList().leftPush(userId, url.toExternalForm());
}
}
专注于字符串的便捷类 {#redis:string}
由于在 Redis 中存储的键和值通常是 java.lang.String
类型,Redis 模块提供了对 RedisConnection
和 RedisTemplate
的两个扩展,分别是 StringRedisConnection
(及其 DefaultStringRedisConnection
实现)和 StringRedisTemplate
,作为密集型字符串操作的便捷一站式解决方案。除了绑定到 String
类型的键之外,该模板和连接在底层使用了 StringRedisSerializer
,这意味着存储的键和值是人类可读的(假设 Redis 和你的代码使用了相同的编码)。以下清单展示了一个示例:
- Java Imperative
- Java Reactive
- XML
@Configuration
class RedisConfiguration {
@Bean
LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
@Configuration
class RedisConfiguration {
@Bean
LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
ReactiveStringRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory factory) {
return new ReactiveStringRedisTemplate<>(factory);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="redisConnectionFactory"/>
</beans>
- Imperative
- Reactive
public class Example {
@Autowired
private StringRedisTemplate redisTemplate;
public void addLink(String userId, URL url) {
redisTemplate.opsForList().leftPush(userId, url.toExternalForm());
}
}
public class Example {
@Autowired
private ReactiveStringRedisTemplate redisTemplate;
public Mono<Long> addLink(String userId, URL url) {
return redisTemplate.opsForList().leftPush(userId, url.toExternalForm());
}
}
与其他 Spring 模板一样,RedisTemplate
和 StringRedisTemplate
允许你通过 RedisCallback
接口直接与 Redis 进行交互。这一特性为你提供了完全的控制权,因为它直接与 RedisConnection
进行通信。需要注意的是,当使用 StringRedisTemplate
时,回调接收的是一个 StringRedisConnection
的实例。以下示例展示了如何使用 RedisCallback
接口:
public void useCallback() {
redisOperations.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
Long size = connection.dbSize();
// Can cast to StringRedisConnection if using a StringRedisTemplate
((StringRedisConnection)connection).set("key", "value");
}
});
}
序列化器 {#redis:serializer}
从框架的角度来看,Redis 中存储的数据只是字节。虽然 Redis 本身支持多种类型,但在大多数情况下,这些类型指的是数据的存储方式,而不是它所代表的内容。由用户决定是否将这些信息转换为字符串或其他对象。
在 Spring Data 中,用户(自定义)类型与原始数据之间的转换(以及反之)由 Spring Data Redis 中的 org.springframework.data.redis.serializer
包处理。
这个包包含两种类型的序列化器,顾名思义,它们负责处理序列化过程:
-
基于 RedisSerializer 的双向序列化器。
-
使用
RedisElementReader
和RedisElementWriter
的元素读写器。
这些变体的主要区别在于,RedisSerializer
主要将数据序列化为 byte[]
,而 reader 和 writer 使用 ByteBuffer
。
有多种实现方式可供选择(包括本文档中已经提到的两种):
-
JdkSerializationRedisSerializer,默认用于 RedisCache 和 RedisTemplate。
-
StringRedisSerializer
。
然而,可以通过 Spring 的 OXM 支持使用 OxmSerializer
进行对象/XML 映射,或者使用 Jackson2JsonRedisSerializer 或 GenericJackson2JsonRedisSerializer 以 JSON 格式存储数据。
请注意,存储格式不仅限于值。它可以不受任何限制地用于键、值或哈希。
默认情况下,RedisCache 和 RedisTemplate 配置为使用 Java 原生序列化。Java 原生序列化以允许通过利用易受攻击的库和类注入未经验证的字节码的负载来运行远程代码而闻名。在反序列化步骤中,被操纵的输入可能导致在应用程序中运行不需要的代码。因此,不要在不受信任的环境中使用序列化。一般来说,我们强烈建议使用其他任何消息格式(如 JSON)代替。
如果您担心由于 Java 序列化导致的安全漏洞,请考虑在核心 JVM 级别使用通用序列化过滤机制: