1. RedisTemplate快速开发

spring-redis对redis的五种数据类型是支持的。这个几个数据操作对象在demo的RedisConfig类中配置了,具体可前去查看代码。本文最重要的点是Object的序列化,下文会着重分析。

  • HashOperations:对hash类型的数据操作

  • ValueOperations:对redis字符串类型数据操作

  • ListOperations:对链表类型的数据操作

  • SetOperations:对无序集合类型的数据操作

  • ZSetOperations:对有序集合类型的数据操作

配置pom依赖

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!-- 引入 redis 依赖 -->
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-data-redis</artifactId>
	</dependency>
	<dependency>
	    <groupId>org.apache.commons</groupId>
	    <artifactId>commons-pool2</artifactId>
	</dependency>
	
	<dependency>
	    <groupId>com.alibaba</groupId>
	    <artifactId>fastjson</artifactId>
	    <version>1.2.44</version> 
	</dependency>
</dependencies>

redis配置信息application.properties

前面是redis服务器的ip,端口等参数信息。后面的是lettuce连接池的配置参数。

#redis db
spring.redis.redis-onedb.database=0
spring.redis.redis-onedb.hostName=localhost
spring.redis.redis-onedb.port=6379
spring.redis.redis-onedb.timeout=5000

spring.redis.lettuce.pool.MaxTotal=50
spring.redis.lettuce.pool.minIdle=1
spring.redis.lettuce.pool.maxWaitMillis=5000
spring.redis.lettuce.pool.maxIdle=5
spring.redis.lettuce.pool.testOnBorrow=true
spring.redis.lettuce.pool.testOnReturn=true
spring.redis.lettuce.pool.testWhileIdle=true

配置类RedisTemplateConfig

配置类中的bean包括:lettuce连接池参数对象GenericObjectPoolConfig,单例redis配置参数对象RedisStandaloneConfiguration,连接对象工厂LettuceConnectionFactory,以及redisTemplate。

GenericObjectPoolConfig:连接池参数配置类,SpringBoot升级到2.0后就不再使用jedis连接池。默认选择的是lettuce。

RedisStandaloneConfiguration:redis单例服务器的参数配置类。

LettuceConnectionFactory:redis连接对象创建工厂。

RedisTemplate:SpringBoot用来封装Redis所有数据类型的操作,供业务程序调用。

@Configuration
public class RedisTemplateConfig {
	
	
	@Bean
	@ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
	@Scope(value = "prototype")
	public GenericObjectPoolConfig redisPool(){
	    return new GenericObjectPoolConfig();
	}
	
	@Bean
	@ConfigurationProperties(prefix = "spring.redis.redis-onedb")
	public RedisStandaloneConfiguration redisConfigA(){
	    return new RedisStandaloneConfiguration();
	}
	
	
	@Primary
	@Bean
	public LettuceConnectionFactory factoryA(GenericObjectPoolConfig config, RedisStandaloneConfiguration redisConfigA){
	    LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder()
	            .poolConfig(config).commandTimeout(Duration.ofMillis(config.getMaxWaitMillis())).build();
	    return new LettuceConnectionFactory(redisConfigA, clientConfiguration);
	}
	
	
	@Bean(name = "stringRedisTemplate")
	public StringRedisTemplate redisBusTemplate(@Qualifier("factoryA") LettuceConnectionFactory factoryA){
	    StringRedisTemplate template = getRedisTemplate();
	    template.setConnectionFactory(factoryA);
	    return template;
	}

	private StringRedisTemplate getRedisTemplate(){
	    StringRedisTemplate template = new StringRedisTemplate();
	    template.setValueSerializer(new GenericFastJsonRedisSerializer());
	    template.setValueSerializer(new StringRedisSerializer());
	    return template;
	}

//    /**
//     * 选择redis作为默认缓存工具
//     * @param redisTemplate
//     * @return
//     */
//    @Bean
//    public CacheManager cacheManager(RedisTemplate redisTemplate) {
//        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//        return rcm;
//    }

    /**
     * retemplate相关配置
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(@Qualifier("factoryA")LettuceConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());

        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();

        return template;
    }

    /**
     * 对hash类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    /**
     * 对redis字符串类型数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    /**
     * 对链表类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    /**
     * 对无序集合类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    /**
     * 对有序集合类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

至此,redisTemplate实例就配置好了,可以直接引用redisTemplate,就能操作redis所有数据类型的api,与redis服务器交互。

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Autowired
private RedisTemplate redisTemplate;

2. 两种实例在redis中存储的数据对比

这两种redis操作实例有什么不同呢,这个我们可以来测一下,测试类如下代码所示。

@Component
public class RedisTestor implements ApplicationRunner
{
	@Autowired
	private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

	@Override
	public void run(ApplicationArguments args) throws Exception {
		// TODO Auto-generated method stub
		
		System.out.println("使用stringRedisTemplate设置stringRedisTemplate键的值为hello stringRedisTemplate");
		stringRedisTemplate.opsForValue().set("stringRedisTemplate", "hello stringRedisTemplate");
		System.out.println("使用redisTemplate设置redisTemplate键的值为hello redisTemplate");
		redisTemplate.opsForValue().set("redisTemplate", "hello redisTemplate");
//		
//		System.out.println("db1中key:"+oneRedis.opsForValue().get("key"));
//		System.out.println("db2中key:"+twoRedis.opsForValue().get("key"));
		
	}

}

分别使用RedisTemplate实例与StringRedisTemplate实例 set进redis两个数据,两个key分别是:stringRedisTemplate,redisTemplate。

查看redis中数据

alt

我们可以看到两个key的value值不一样:redisTemplate实例对应的值两边多了反斜杠和双引号。这就是value的序列化工具不同导致的。

StringRedisTemplate的value序列化工具是StringRedisSerializer。

template.setValueSerializer(new StringRedisSerializer());

redisTemplate的value序列化工具是Jackson2JsonRedisSerializer。

// 值采用json序列化
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
template.setValueSerializer(jacksonSeial);

这两种不同的value序列化工具,导致了value在redis中的存在格式不一样。不同在于数据的可读性好与坏。

对Info类进行redis存取

编写测试代码

System.out.println("测试object在redis中的存取");

Info info = new Info();
info.setInfo1_private("Info1_private");
info.setInfo2_private("info2_private");
info.setInfo3_private(3);

System.out.println("使用redisTemplateA设置redisTemplateA_Obj键的值为info:"+info.toString());
redisTemplateA.opsForValue().set("redisTemplateA_Obj", info);


System.out.println("使用redisTemplateB设置redisTemplateB_Obj键的值为info");
redisTemplateB.opsForValue().set("redisTemplateB_Obj", info);

redis中对象数据

redisTemplateA_Obj使用的是Jackson2JsonRedisSerializer序列化工具,redisTemplateA_Obj使用的是JdkSerializationRedisSerializer序列化工具。

127.0.0.1:6379> get redisTemplateB_Obj
"xacxedx00x05srx00x1dRedis.RedisTemplate.bean.Infoxeex99xc0xa1Axd2x0fxa0x02x00x03Lx00
info1_privatetx00x12Ljava/lang/String;Lx00
info2_privateqx00~x00x01Lx00
info3_privatetx00x13Ljava/lang/Integer;xptx00
Info1_privatetx00
info2_privatesrx00x11java.lang.Integerx12xe2xa0xa4xf7x81x878x02x00x01Ix00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x00x03"
127.0.0.1:6379> get redisTemplateA_Obj
"["Redis.RedisTemplate.bean.Info",{"info1_private":"Info1_private","info2_private":"info2_private","info3_private":3}]"
127.0.0.1:6379>

对比可知,Jackson2JsonRedisSerializer序列化数据可读性好,使用JdkSerializationRedisSerializer序列化的数据可读性太差。

3. 用RedisTemplate还是StringRedisTemplate

这两种实例的选择,还是要根据具体业务场景来抉择。 如果业务数据只是简单的String类型,例如登入校验鉴权token场景,我们就只使用StringRedisTemplate实例即可。如果业务场景数据结构复杂,那么我们就应该选择RedisTemplate实例,这个实例能让我们直接存取对象数据,不需要将数据进行转换。

第二个要考虑的是数据的可读性,这个主要取决于序列化工具的选择。Jackson2JsonRedisSerializer序列化可读性强,有助于运维。

4. 总结

提醒一个问题,如果你使用的是RedisTemplate,操作Object类型数据,你可能会遇到一个问题:操作Object类失败。

这时候,你应该去检查下你的Object的类是否实现了序列化接口,因为在Java体系中,所有能被序列化的类都需要实现java.io.Serializable。关于StringRedisTemplate , RedisTemplate 的配置和测试都有验证成功的代码,日后需要的话 拿来即用。收藏起来吧!

查看更多 “Java架构师方案” 系列文章 以及 SpringBoot2.0学习示例

完整的demo项目,请关注公众号“前沿科技bot“并发送"RedisTemplate"获取。

alt