连接到Redis
使用Redis和Spring时的首要任务之一是通过IoC容器连接到Redis。为此,需要java连接器(或绑定)。无论选择哪种库,你都只需要使用一组Spring Data Redis API(在所有连接器中行为一致):org.springframework.data.redis.connection软件包及其RedisConnection与RedisConnectionFactory接口,用于处理和检索与Redis的活动连接。
RedisConnection和RedisConnectionFactory
RedisConnection提供了Redis通信和核心构建块,因为它处理与Redis后端的通信。它还会自动将基础链接库异常转换为Spring一致的DAO异常层次结构,以便您可以在不更改任何代码的情况下切换连接器,因为操作语义保持不变。
当按照上篇文档配置好Redis后,IOC会加载ConnectionFactory,我们可以直接注入,然后创建连接操作Redis。
RedisConnection提供了Redis 各大数据类型的操作API:
java
public interface RedisConnection extends RedisCommands, AutoCloseable {
default RedisGeoCommands geoCommands() {
return this;
}
default RedisHashCommands hashCommands() {
return this;
}
default RedisHyperLogLogCommands hyperLogLogCommands() {
return this;
}
default RedisKeyCommands keyCommands() {
return this;
}
default RedisListCommands listCommands() {
return this;
}
default RedisSetCommands setCommands() {
return this;
}
default RedisScriptingCommands scriptingCommands() {
return this;
}
default RedisServerCommands serverCommands() {
return this;
}
default RedisStreamCommands streamCommands() {
return this;
}
default RedisStringCommands stringCommands() {
return this;
}
default RedisZSetCommands zSetCommands() {
return this;
}
}
java
@Autowired
LettuceConnectionFactory lettuceConnectionFactory;
@Test
void lettuceConnectionFactoryTest() {
RedisConnection connection = lettuceConnectionFactory.getConnection();
Boolean result = connection.set("k".getBytes(), "1".getBytes());
System.err.println(result);
byte[] bytes = connection.get("k".getBytes());
assert bytes != null;
System.err.println("k:" + new String(bytes));
}
RedisTemplate简介
大多数用户可能会使用RedisTemplate及其相应的软件包org.springframework.data.redis.core。实际上,由于模版具有丰富的功能集,因此它是Redis模块的中心类。该模板为Redis交互提供了高级抽象,虽然RedisConnection提供了接受和返回二进制值(byte数组)的低级方法,但是模板负责序列化和连接管理,使用户无需处理此类细节。
此外,该模板提供了操作视图(根据Redis命令参考进行分组),提供了丰富的,通用的接口,用于针对某种类型或某些键(通过keyBound接口),如下表所述:
|-----------------------|----------------------------------------|
| 界面 | 描述 |
| | 按键类型操作 |
| GeoOperations | Redis的地理空间操作的,比如GEOADD,GEORADIUS... |
| HashOperations | Redis哈希操作 |
| HyperLogLogOperations | Redis的HyperLogLog操作,例如PFADD,PFCONT,... |
| ListOperations | Redis列表操作 |
| SetOperations | Redis设置操作 |
| ValueOperations | Redis字符串(或值)操作 |
| ZSetOperations | Redis zset(或排序集)操作 |
| | 关键绑定操作 |
| BoundGeoOperations | Redis键绑定地理空间操作 |
| BoundHashOperations | Redis哈希键绑定操作 |
| BoundKeyOperations | Redis按键绑定操作 |
| BoundListOperations | Redis列表键绑定操作 |
| BoundSetOperations | Redis设置键绑定操作 |
| BoundValueOperations | Redis字符串(或值)键绑定操作 |
| BoundZSetOperations | Redis zset(或排序集)键绑定操作 |
序列化器
自带序列化器
RedisTemplate大多数操作都使用基于Java的序列化器。这意味着模板编写或读取的任何对象都将通过Java进行序列化和反序列化。你可以在模板上更改序列化机制,Redis模块提供了几种实现,可在org.springframework.data.redis.serializer软件包中找到,您还可以将任何序列化器设置为null,并通过将enableDefaultSerializer属性设置为来将RedisTemplate与原始字节数组一起使用False。请注意,模板要求所有键都不为空。但是,只要基础串行器接受这些值,它们就可以为空。
从框架的角度来看,Redis中存储的数据仅为字节。尽管Redis本身支持各种类型,但在大多数情况下,它们是指数据的存储方式而不是数据的表示方式。由用户决定是否将信息转换为字符串或任何其他对象。
在Spring Data中,用户(自定义)类型和原始数据之间的转换(反之亦然)在org.springframework.data.redis.serializer包中的Redis中进行处理。
该软件包包含两种类型的序列化器,它们负责序列化过程:
- 基于的两路串行器RedisSerializer。
- 使用RedisElementReader和的元素读取器和写入器RedisElementWriter。
框架自带各种序列化器:
|------------------------------------|------------------------------------------------------------------|
| 名称 | 说明 |
| OxmSerializer | 通过Spring OXM支持将其用于对象/XML映射 |
| ByteArrayRedisSerializer | Byte数组序列化 |
| GenericJackson2JsonRedisSerializer | 以JSON格式去存储数据,会保存序列化的对象的包名和类名,反序列化时以这个作为标示就可以反序列化成指定的对象。效率低,占用内存高 |
| GenericToStringSerializer | 可以将任何对象泛化为字符串并序列化 |
| StringRedisSerializer | 简单的字符串序列化 |
| JdkSerializationRedisSerializer | JDK序列化,默认用于RedisCache和RedisTemplate |
| Jackson2JsonRedisSerializer | 以JSON格式去存储数据,需要指明序列化的类Class,可以使用Object.class |
自定义序列化器
RedisTemplate默认使用JDK序列化,可以使用自带其他的序列化,或者自己实现第三方序列化方式,比如:
- google:Protobuf
- faceBook:Thrift
- kryo
- hessian
- fst
- Jackson
- Gson
- FastJson
Protobuf序列化
ProtoBuf(Google Protocol Buffer)是由Google公司用于数据交换的序列结构化数据格式,具有跨平台、跨语言、可扩展特性,类型于常用的XML及JSON,但具有更小的传输体积、更高的编码、编码能力,特别适合于数据存储、网络数据传输等对存储体积、实时性要求高的领域。
优点:性能好,效率高。支持向后兼容和向前兼容。支持多种编程语言(Java,C++,python);
缺点:二进制格式导致可读性差(二进制格式)
RedisTemplate使用Protobuf
1、添加POM;
java
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
1、 实现RedisSerializer接口;
java
@Slf4j
public class ProtoStuffRedisSerializer<T> implements RedisSerializer<T> {
// RuntimeSchema是一个包含业务对象所有信息的类,包括类信息、字段信息
private static final Schema<ProtoStuffWrapper> schema = RuntimeSchema.getSchema(ProtoStuffWrapper.class);
/**
* 序列化:对象=》字节数组
*
* @param t 需要序列化的对象t
* @return 二进制
* @throws SerializationException 序列化异常
*/
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return null;
}
// 开辟了512字节缓存,用来存放业务对象序列化之后存放的地方,如果空间不足,会自动扩展扩展
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
byte[] bytes;
try {
// 序列化
bytes = ProtostuffIOUtil.toByteArray(new ProtoStuffWrapper<>(t), schema, buffer);
} finally {
buffer.clear();
}
return bytes;
}
/**
* 反序列化 字节数组=》对象
*
* @param bytes 字节数组
* @return 对象
* @throws SerializationException 序列化异常
*/
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length == 0) {
return null;
}
try {
ProtoStuffWrapper<T> protoStuffWrapper = new ProtoStuffWrapper<>();
// 反序列
ProtostuffIOUtil.mergeFrom(bytes, protoStuffWrapper, schema);
return protoStuffWrapper.getT();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 序列化包装类,深度克隆,避免无法获取schema
*
* @param <T> 业务对象
*/
public static class ProtoStuffWrapper<T> implements Cloneable {
private T t;
ProtoStuffWrapper() {
}
ProtoStuffWrapper(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
@Override
@SuppressWarnings("unchecked")
public ProtoStuffWrapper<T> clone() {
try {
return (ProtoStuffWrapper<T>) super.clone();
} catch (CloneNotSupportedException e) {
return new ProtoStuffWrapper<T>();
}
}
}
}
1、 创建RedisTemplate,设置序列化;
java
/**
* 创建RedisTemplate
*
* @param redisConnectionFactory 连接工厂
* @param <T> 值类型
* @return RedisTemplate
*/
@Bean(name = "redisTemplate")
public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
// String序列化对象
RedisSerializer<String> stringRedisSerializer = RedisSerializer.string();
// ProtoStuff序列化
ProtoStuffRedisSerializer<T> protoStuffRedisSerializer = new ProtoStuffRedisSerializer<>();
// 序列化配置=>Key
redisTemplate.setKeySerializer(stringRedisSerializer); // 所有Key都设置为字符串,方便阅读
redisTemplate.setHashKeySerializer(protoStuffRedisSerializer); // 设置Hash数据结构中的Key
// 序列化配置=>Value
redisTemplate.setValueSerializer(protoStuffRedisSerializer); // 所有Value
redisTemplate.setHashValueSerializer(protoStuffRedisSerializer); // Hash数据结构中的Value
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
1、 添加测试实体类;
java
@ToString
@Data
public class User {
String userName;
String password;
int age;
}
1、 添加测试类测试;
java
@Test
void protoStuffTest() {
User user = new User();
user.setAge(20);
user.setUserName("韩梅梅");
user.setPassword("123456");
redisTemplate.boundValueOps("k").set(user);
User user1 = redisTemplate.boundValueOps("k").get();
redisTemplate.boundHashOps("hash").putIfAbsent("kkk","vvv");
System.err.println(user1.toString());
}