序列化

我们在前面使用过 StringRedisTemplate 与 RedisTemplate 模板,不过在使用时,没有单独设置过序列化器。其中,在操作字符串时,使用 StringRedisTemplate 即可,如果是操作其他数据类型,这个模板就不再适用,则需要换成 RedisTemplate 模板。除了默认的序列化器,还可以灵活地设置其他序列化器。

序列化实例

在 Spring Data Redis 中,默认采用的序列化器有两种,一种是 JDK 的序列化器,另一种是 String 的序列化器。其实,在 Spring 中还有别的序列化器,关于其他的序列化器,我们可以看下面的 org.springframework.data.redis.serializer。首先,我们看一个自定义序列化的实例。

配置application.properties

本节主要讲解在 Spring Boot 中使用 Redis。我们先前讲解的工厂操作,以及对数据类型的操作,都是基于上文的工厂操作获取模板往下继续测试的。也许读者一直存在着疑问,这个 Spring Boot 的配置项在哪里?说好的开箱即用体现在哪里?在这个实例,以及后面的实例中,我们就解答这两个问题,先来看看 Redis 的配置。

#Redis的配置
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.min-idle=0

spring.redis.database 的意思是使用数据库,在 Redis 中,默认一共有 16 个数据库,一般情况下使用默认的 0;host 的意思是 Redis 的服务器地址,默认为 127.0.0.1;port 是 Redis 的端口,默认为 6379。

我们使用的 Spring Boot 版本为 2.×,与前面 1.× 有些不同,主要体现在 pool 上,在使用时注意一下即可,因为 Spring Boot 2.× 中 pool 的属性已经被封装到 Redis 或者 Lettuce 中。

max-active 的意思是连接池最大连接数,负数则表示没有限制;max-wait 意思是最大阻塞等待时间;min-idle 的意思是连接池中的最小空闲连接数。

其实,还有属性 max-idle 表示最大空闲连接数。有时候连接超时时间也会设置,即 spring.redis.timeout,单位为秒。

这里需要注意,因为在 Spring Boot 1.× 中,参数类型是 int,单位是毫秒,只需要配置数值即可,例如 18000。但是,在 Spring Boot 2.× 中,时间的相关配置类型修改为 JDK 的 Guration,因此在写连接超时时间时,需要写上单位。

序列化

在 Spring Boot 中使用 Redis 的时候,不管使用什么方式,最后都会把 key 与 value 使用字符串或者序列化成 byte 数组进行保存,所以在进行数据操作的时候,才会考虑到序列化的问题。

在上文讲解数据操作的时候,我们定义了自己的对象,这时不再刻意使用 StringRedisTemplate,而是使用 RedisTemplate 进行操作。但是针对 Object,还是需要使用 JDK 的序列化才可以进行操作。

在上文操作 Redis 的数据类型,使用 RedisTemplate 时,没有进行过序列化,这并不代表 JDK 序列化完美,不需要再考虑这个问题,因为效率问题也是需要考虑的。在 JDK 序列化中,被序列化的对象除了属性的内容,还包含其他的内容,因此长度长,而且不方便阅读。

下面,先讲解一个普通的用法,使用模板将其修改为自己想用的序列化器。首先写一个对象类,代码如下所示。

package com.springBoot.redis.entity;

public class Employ {
   private String id;
   private String name;
   //GET和SET方法
   @Override
   public String toString() {
      return "Employ{" +
             "id='" + id + '\'' +
             ", name='" + name + '\'' +
             '}';
   }
}

然后,写一个测试类,代码如下所示。

package com.springBoot.redis;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisApplicationTest {
   @Autowired
   RedisTemplate redisTemplate;
   @Test
   public void testSerializer(){
      String name="employ";
      //获取hash操作接口
      redisTemplate.setKeySerializer(new StringRedisSerializer());
      redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
      HashOperations<String,String,Employ> hashOperations=redisTemplate.opsForHash();
      //存放数据
      hashOperations.put(name,"employ1",new Employ("1","a1"));
      hashOperations.put(name,"employ2",new Employ("2","a2"));
      hashOperations.put(name,"employ3",new Employ("3","a3"));
      //
      Long size=hashOperations.size(name);
      System.out.println("size: "+size); //大小
      Set<String> keys=hashOperations.keys(name); //拿到Map的key集合
      for (String str:keys){
          System.out.println("str: "+str);
      }
      List<Employ> employs=hashOperations.values(name);
      for (Employ employ:employs){
          System.out.println("employ: "+employ.toString());
      }
   }
}

在上文的替换过程中,只需要使用 SET 就可以将其实现,执行结果如图8.10所示。

image 2024 03 31 22 47 32 245
Figure 1. 图8.10 执行结果

在上面的代码中,为了方便,直接在 template 后面设置,这种情况适合单个实例的演示。在实际中,建议将这个写成 Bean,可以在多个地方使用,不再写重复代码。

对于改进办法,这里写一个示例作为参考。我们使用 Jackson2JsonRedisSerializer 序列化器作为实例,将数据序列化成 json,代码如下所示。

@Configuration
public class RedisConfig {
   @Bean
   JedisConnectionFactory connectionFactory() {
      return new JedisConnectionFactory();
   }
   @Bean
   public RedisTemplate redisTemplate(JedisConnectionFactory connectionFactory) {
      RedisTemplate redisTemplate = new RedisTemplate();
      redisTemplate.setConnectionFactory(connectionFactory);
      //Jackson2JsonRedisSerializer
      Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =new Jackson2JsonRedisSerializer(Object.class);
      ObjectMapper objectMapper = new ObjectMapper();
      jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
      redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
      redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
      redisTemplate.setKeySerializer(new StringRedisSerializer());
      redisTemplate.setHashKeySerializer(redisTemplate. getKeySerializer());
      redisTemplate.afterPropertiesSet();
      return redisTemplate;
   }
}

序列化讲解

保存数据后,进行序列化操作。在 Spring Data Redis 中,有一些序列化器可以供我们使用,目前在项目中实现 RedisSerializer 接口的常用的序列化器有 6 个,分别如下。

  • GenericToStringSerializer:使用 String 转换进行序列化。

  • GenericJackson2JsonRedisSerializer:使用 Json2,序列化为 Json。

  • Jackson2JsonRedisSerializer:使用 Json2,序列化为 Json。

  • JdkSerializationRedisSerializer:使用 JDK 自带的序列化。

  • OxmSerializer:使用 O/X 映射的解码器与编码器序列化,主要用于 XML。

  • StringRedisSerializer:序列化 String 类型的键与值。

接下来介绍常用的 4 个。

GenericJackson2JsonRedisSerializer与Jackson2JsonRedisSerializer

这两个序列化器都可以将数据序列化为 Json,但也有区别。前者可以在 Json 数据中加入 @class 属性,以及类的包名和路径,方便了序列化。如果存储的类型为 List 等带有泛型的对象,反序列化的时候 Jackson2JsonRedisSerializer 序列化方式会报错,而 GenericJ ackson2JsonRedisSerializer 序列化方式会成功。

在 GenericJackson2JsonRedisSerializer 中存在 @class 属性,则可以找到需要转换的类型,序列化成功。但是 Jackson2JsonRedisSerializer 的效率高于 GenericJackson2JsonRedisSerializer。使用时根据需要自己选择。

GenericToStringSerializer与StringRedisSerializer

这两个序列化器也稍微说明一下。GenericToStringSerializer 可以将一切对象泛化为 String 进行序列化,即使用 Spring 转换服务进行序列化。

但是 StringRedisSerializer 只是简单地字符串序列化,操作 String 数据。有兴趣的读者可以自己测试一下效果。