10 springBoot操作Redis

1 springBoot整合Redis

引入对应的starter即可

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

1.1 自动配置类

package org.springframework.boot.autoconfigure.data.redis;

import java.net.UnknownHostException;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;


@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}
  • @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    导入了这么两个配置类
    • LettuceConnectionConfiguration生效的条件是classpath下有RedisClient这个类
    • RedisConnectionConfiguration生效的条件是classpath有jedis相关的类
    • springboot2之后默认使用的就是Lettuce客户端,而springboot1默认使用的是jedis客户端
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RedisClient.class)
    class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
    
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
    class JedisConnectionConfiguration extends RedisConnectionConfiguration 
    
  • 如何替换为jedis客户端呢?排除lettuce的依赖,导入jedis的依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
  • RedisAutoConfiguration,自动导入了两个组件: RedisTemplate和StringRedisTemplate

    • StringRedisTemplate是RedisTemplate的子类,两个方法基本一致,不同之处主要体现在操作的数据类型不同
    • RedisTemplate中的两个泛型都是Object,意味着存储的key和value都可以是一个对象,
    • 而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的key和value都只能是字符串。
  • spring封装了RedisTemplate这个组件来提供给我们操作redis的数据

    • 之前看jedis操作redis,无论value是字符串还是list等其他数据类型,其实都是字符串,list这种集合类型的数据类型
      的元素也是字符串
    • 但是我们操作来说都是对象,spring 通过RedisTemplate帮助我们实现了对象的序列化和反序列化,所以使用 RedisTemplate默认是将对象序列化到Redis中,所以放入的对象必须实现对象序列化接口
  • 如何配置:RedisProperties,可以根据这个类来配置我们的yml配置文件,比如:

spring:
  redis:
    host: localhost # 默认值
    port: 6379 #默认值

2 StringRedisTemplate基本操作

就是操作字符串,这个字符串不是简单说操作redis的字符串类型,说的是数据的类型,比如list中的元素是字符串类型

package study.wyy.redis.boot.test.test02;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import study.wyy.redis.boot.test.BaseTest;

import javax.lang.model.element.VariableElement;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class StringRedisTemplateTest extends BaseTest {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Test
    public void testKey(){

        // 1 是否存在指定的key
        Boolean isExist = stringRedisTemplate.hasKey("name");
        // 2 获取所有的key
        stringRedisTemplate.keys("*");
        // 3 删除key
        stringRedisTemplate.delete("name");
        // 4 获取key的过期时间
        Long name = stringRedisTemplate.getExpire("name");
        // 5 设置key的有效时间
        stringRedisTemplate.expire("name",100, TimeUnit.MILLISECONDS);

    }

    @Test
    public void testString(){
        // 1 设置数据
        stringRedisTemplate.opsForValue().set("name","wyy");
        // 如果当前的key不存在,才会设置数据
        stringRedisTemplate.opsForValue().setIfAbsent("name","laoliu ");
        // 2 append
        stringRedisTemplate.opsForValue().append("name", "2020");
        // 3 获取数据
        String name = stringRedisTemplate.opsForValue().get("name");
        System.out.println("name = " + name);
        // 4 设置数据并设置过期时间
        stringRedisTemplate.opsForValue().set("gender","man",3,TimeUnit.SECONDS);
        // 5 截取字符串结果 闭区间 [0,2]
        name = stringRedisTemplate.opsForValue().get("name", 0, 2);
        System.out.println("name = " + name);

        // 数值操作
        stringRedisTemplate.opsForValue().set("age","1");
        stringRedisTemplate.opsForValue().increment("age"); //2
        stringRedisTemplate.opsForValue().increment("age",2);  //4
        String age = stringRedisTemplate.opsForValue().get("age");
        System.out.println("age = " + age);

    }


    @Test
    public void testList(){
        stringRedisTemplate.delete("languages");
        // 1 lpush 数据
        stringRedisTemplate.opsForList().leftPush("languages","java");
        stringRedisTemplate.opsForList().leftPushAll("languages","html","python");
        // 2 必须要保证key存在参会添加数据
        stringRedisTemplate.opsForList().leftPushIfPresent("languages","c#");
        // 3 lrange
        List<String> languages = stringRedisTemplate.opsForList().range("languages", 0, -1);
        System.out.println(languages);
        // 8 获取list的长度
        Long size = stringRedisTemplate.opsForList().size("languages");
        System.out.println("长度 = " + size);

        // 4 lindex
        String languages1 = stringRedisTemplate.opsForList().index("languages", 0);
        System.out.println(languages1);

        // 5 lpop
        String res = stringRedisTemplate.opsForList().leftPop("languages");
        System.out.println(res);
        // 6 rem :删除指定元素,并且可以指定个数,
        Long remove = stringRedisTemplate.opsForList().remove("languages", 1, "html");

        // 7 ltrim: 保留列表中特定区间内的元素(依然是左边开始数),索引计数是从左边开始的
        stringRedisTemplate.opsForList().trim("languages",0,3);

        // 3 lrange
        languages = stringRedisTemplate.opsForList().range("languages", 0, -1);
        System.out.println(languages);

    }   
}

总结:

  • 上面只演示了String和List的基本操作,其他redis数据类型也是如此一般,不过多介绍
  • 提供了key相关的api
  • 提供了redis,list,map,set等数据类型的操作,规律:
    • opsForValue:这是redis的String类型
    • opsForList:: 操作list
    • opsForHash: 操作map
    • opsForSet:操作set
    • opsForZset:操作zset
    • 还有opsFor的其他相似的函数,比如opsForCluster,对redis集群的操作
  • 所有的数据的组成都是字符串,lisrt中的元素都是字符串类型,所以叫stringRedisTemplate

3 RedisTemplate基本操作

3.1 StringRedisTemplate和RedisTemplate区别

@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
  • 泛型不同,泛型指定的分别是key的格式,以及value中数据的格式
    • 二者的使用没有太多区别,只是StringRedisTemplate<String,String>:key是String格式,这也是redis中key的格式;value中数据的格式也是String
      • 和jedis的使用差不多,jedis在操作redis的时候,就是这种数据格式操作
    • RedisTemplate<Object, Object>:key是Object格式,value中数据的格式也是Object格式,java开发更多时候拿到的是对象,因而更方便java程序员操作,
      • spring实现了序列化,在向redis存取数据的时候,对key和value分别做了序列化
      • 所以存入redis的对象必须实现序列化接口

RedisTemplate的源码可以发现:

public RedisTemplate() {}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.RedisAccessor#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() {

		super.afterPropertiesSet();

		boolean defaultUsed = false;

		if (defaultSerializer == null) {

			defaultSerializer = new JdkSerializationRedisSerializer(
					classLoader != null ? classLoader : this.getClass().getClassLoader());
		}

		if (enableDefaultSerializer) {	
  • 默认的序列化方式:JdkSerializationRedisSerializer

3.2 RedisTemplate序列化问题

  1. 为了说明问题,先把redis的数据清空,防止其他数据的问题
  2. 运行下面测试代码
public class RedisTemplateTest extends BaseTest {

    @Autowired
    RedisTemplate redisTemplate;

    @Test
    public void test1() {
        User user = new User("kobe", 18);
        // redis中存一个数据
        redisTemplate.opsForValue().set("user", user);
        User user1 = (User) redisTemplate.opsForValue().get("user");
        System.out.println(user1);
    }
}

@AllArgsConstructor
@ToString
@Data
@NoArgsConstructor
// 实现序列化接口
class User implements Serializable {
    private String name;
    private Integer age;
}
  1. 这个时候通过redis客户端是无法获取数据的
    在这里插入图片描述
  2. 可以通过redisTemplate还是可以获取到,因为获取的时候,redisTemplate会序列化key,但是在redis客户端就不好获取了,所以实际开发中,我们不期望key被序列化

3.4 设置RedisTemplate的key的序列化方式

  1. org.springframework.data.redis.serializer.RedisSerializer: spring提供序列化接口,并且提供了多种实现,而RedisTemplate默认使用的就是JdkSerializationRedisSerializer
  2. StringRedisTemplate使用的序列化方式:StringRedisSerializer
    	public StringRedisTemplate() {
    	// 设置key的序列化方式
    	setKeySerializer(RedisSerializer.string());
    	// 设置value的序列化方式
    	setValueSerializer(RedisSerializer.string());
    	// 设置map类型的key的序列化方式,map中也有一个key
    	setHashKeySerializer(RedisSerializer.string());
    	setHashValueSerializer(RedisSerializer.string());
    }
    // RedisSerializer类中的string()方法
    static RedisSerializer<String> string() {
    	return StringRedisSerializer.UTF_8;
    }
    
  3. 修改RedisTemplate中的key的序列化方式
@Test
public void test2() {
    redisTemplate.setKeySerializer(RedisSerializer.string());
    User user = new User("kobe", 18);
    redisTemplate.opsForValue().set("user", user);
    User user1 = (User) redisTemplate.opsForValue().get("user");
    System.out.println(user1);
}

在这里插入图片描述
4. 别忘了hash类型,还有一个key,所以一般也会把这个key也进行序列化

redisTemplate.setHashKeySerializer(RedisSerializer.string());

3.5 设置RedisTemplate的value的序列化方式

刚刚我们的value,redis客户端的数据也是序列化之后,可读性也比较差,如果序列化的后的格式一个json格式的字符串是不是会更好一点,这样出现问题就比较好排查:

  • RedisSerializer有RedisSerializer.json()方法
  • 但是使用的是jackson做的,所以需要导入jackson的依赖
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
@Test
public void test3() {
    redisTemplate.setKeySerializer(RedisSerializer.string());
    redisTemplate.setHashKeySerializer(RedisSerializer.string());
    // 使用的是GenericJackson2JsonRedisSerializer
    redisTemplate.setValueSerializer(RedisSerializer.json());
    redisTemplate.setHashValueSerializer(RedisSerializer.json());
    User user = new User("kobe", 18);
    redisTemplate.opsForValue().set("user", user);
    User user1 = (User) redisTemplate.opsForValue().get("user");
    System.out.println(user1);
}

在这里插入图片描述

  • 此时我们的User类就必须提供get和set方法,Jackson是通过get和set方法序列化的
  • 还要提供一个无参构造,Jackson通过反射调用无参构造创建对象

3.6 自定义RedisTemplate

通过上面描述的问题,可以考虑自己定义一个RedisTemplate注入到容器中:

  • key: 就可以是个字符串,没必要是个Object
  • value可以考虑序列化为json,提高可读性
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setKeySerializer(RedisSerializer.string());
    redisTemplate.setHashKeySerializer(RedisSerializer.string());
    redisTemplate.setValueSerializer(RedisSerializer.json());
    redisTemplate.setHashValueSerializer(RedisSerializer.json());
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    return redisTemplate;
}

使用:

@Autowired
RedisTemplate<String,Object> redisTemplate;

@Test
public void test1() {
    User user = new User("kobe", 18);
    redisTemplate.opsForValue().set("user", user);
    User user1 = (User) redisTemplate.opsForValue().get("user");
    System.out.println(user1);
}

3.7 spring提供的序列化方式哪些呢(了解)

  1. org.springframework.data.redis.serializer.RedisSerializer: spring提供的序列化的顶层接口

  2. 其实现主要有:

    • GenericJackson2JsonRedisSerializer
    • Jackson2JsonRedisSerializer
    • JdkSerializationRedisSerializer
    • ByteArrayRedisSerializer
    • OxmSerializer
    • GenericToStringSerializer
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值