文章目录
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中,所以
放入的对象必须实现对象序列化接口
- 之前看jedis操作redis,无论value是字符串还是list等其他数据类型,其实都是字符串,list这种集合类型的数据类型
-
如何配置:
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的对象必须实现序列化接口
- 二者的使用没有太多区别,只是StringRedisTemplate<String,String>:key是String格式,这也是redis中key的格式;value中数据的格式也是String
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序列化问题
- 为了说明问题,先把redis的数据清空,防止其他数据的问题
- 运行下面测试代码
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;
}
- 这个时候通过redis客户端是无法获取数据的
- 可以通过redisTemplate还是可以获取到,因为获取的时候,redisTemplate会序列化key,但是在redis客户端就不好获取了,所以实际开发中,我们不期望key被序列化
3.4 设置RedisTemplate的key的序列化方式
org.springframework.data.redis.serializer.RedisSerializer
: spring提供序列化接口,并且提供了多种实现,而RedisTemplate默认使用的就是JdkSerializationRedisSerializer- 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; }
- 修改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提供的序列化方式哪些呢(了解)
-
org.springframework.data.redis.serializer.RedisSerializer
: spring提供的序列化的顶层接口 -
其实现主要有:
- GenericJackson2JsonRedisSerializer
- Jackson2JsonRedisSerializer
- JdkSerializationRedisSerializer
- ByteArrayRedisSerializer
- OxmSerializer
- GenericToStringSerializer