本篇文章会记录一下redis常用api!因为字数超过了限制,所以不得不拆为两部分发出来.....
1、小问题
使用redis的时候发现了一个问题:
redisTemplate.opsForValue().set("test", 22);
Integer test = (Integer) redisTemplate.opsForValue().get("test");
System.out.println(test);
上面代码我增加了一个缓存key:test,然后获取了这个key的值打印出来,结果确实是存入的值:
问题来了,我使用客户端去查看redis的时候,可以看到这个key乱码了:
命令行也获取不到值了:
接下来我就进去set方法debug看了一下,为什么key存进去会乱码呢:
-
进去看看rawValue()做了什么: -
返回去看看key经历了什么:
redis默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略,而JDK序列化策略虽然性能较好,但是不利于在客户端去操作缓存数据,所以有时候我们需要更换redis序列化策略。
2、更改redis序列化方式
在springboot项目中,@SpringBootApplication
注解会帮我们扫描很多配置类:
-
RedisAutoConfiguration:
要更改序列化方式的话,我们需要重写RedisAutoConfiguration
,这个类自身主要的作用是确保以下bean存在于容器中 : RedisTemplate redisTemplate
– 基于容器中的redisConnectionFactory bean
StringRedisTemplate stringRedisTemplate
– 基于容器中的redisConnectionFactory bean
我们需要重新写一个RedisTemplate
,并设置它的序列化方式,为了方便使用以及后续扩展,下面就封装一个starter工程,引入依赖即可使用。
1、新建一个spring-boot-starter-redis的工程
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.7.RELEASE</version>
</parent>
<groupId>com.redis.starter</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>${revision}</version>
<name>spring-boot-starter-redis</name>
<properties>
<java.version>1.8</java.version>
<!-- 设置版本号 -->
<revision>1.0.0.0</revision>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 引入 Redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
2、增加配置类
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnProperty(
value = {
"spring.redis.host",
"spring.redis.port"
}
)
public class RedisAutoConfiguration {
@Autowired
RedisProperties redisProperties;
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置key序列化方式
RedisSerializerKey redisSerializerKey = new RedisSerializerKey(redisProperties.getKeyPrefix());
redisTemplate.setKeySerializer(redisSerializerKey);
redisTemplate.setHashKeySerializer(redisSerializerKey);
// 设置value序列化方式
RedisSerializerValue redisSerializerValue = new RedisSerializerValue();
redisTemplate.setValueSerializer(redisSerializerValue);
redisTemplate.setHashValueSerializer(redisSerializerValue);
return redisTemplate;
}
}
@Data
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
/**
* key前缀(微服务架构,每个服务设置不同前缀,易于区分)
*/
private String keyPrefix;
}
public class RedisSerializerKey extends StringRedisSerializer {
private String keyPrefix;
private final Charset charset;
public RedisSerializerKey(String keyPrefix){
this(keyPrefix, Charset.forName("UTF8"));
}
public RedisSerializerKey(String keyPrefix, Charset charset){
this.keyPrefix = keyPrefix;
this.charset = charset;
}
/**
* 反序列化
*
* @param bytes
* @return
*/
@Override
public String deserialize(byte[] bytes) {
return super.deserialize(bytes);
}
/**
* 序列化
*
* @param string
* @return
*/
@Override
public byte[] serialize(String string) {
if (StringUtils.isEmpty(keyPrefix)){
return super.serialize(string);
}
String key = String.format("%s:%s", keyPrefix, string);
return key.getBytes(charset);
}
}
@Slf4j
public class RedisSerializerValue extends GenericJackson2JsonRedisSerializer {
@Override
public <T> T deserialize(byte[] source, Class<T> type) throws SerializationException {
try {
return super.deserialize(source, type);
}catch (SerializationException e){
log.info("not deserialize of data : {}", source);
}
return null;
}
}
记录几个注解的作用:
-
@Configuration
@Configuration
用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。 -
@EnableConfigurationProperties
@EnableConfigurationProperties
注解使使用了@ConfigurationProperties注解的类生效,如果一个类只使用了@ConfigurationProperties
注解,而没有使用@Component
注解,那么在IOC容器中是获取不到properties配置文件转化的bean。说白了@EnableConfigurationProperties
相当于把使用@ConfigurationProperties
的类进行了一次注入。 -
@ConditionalOnProperty
@ConditionalOnProperty
注解来控制@Configuration
是否生效,有以下几个参数:String[] value() default {}:
数组,获取对应property名称的值,能获取到则该配置类生效,与name不可同时使用String prefix() default "":
配置属性名称的前缀,比如spring.redis,后面的host和post可以以此为开头String[] name() default {}:
数组,配置属性完整名称或部分名称,可与prefix组合使用,组成完整的配置属性名称,能获取到则该配置类生效,与value不可同时使用String havingValue() default "":
可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置boolean matchIfMissing() default false:
缺少该配置属性时是否可以加载。如果为true,没有该配置属性时也会正常加载;反之则不会生效举例:现在有一个配置类如下:
public class ConfigurationTest {
}我想控制这个配置类只有在生产环境才能开启,线下环境是不用开启的,那么就可以使用
@ConditionalOnProperty
注解:@ConditionalOnProperty(prefix = "config", value = "enabled", havingValue = "false")
public class ConfigurationTest {
}该配置类默认关闭,只有在生产环境配置了如下配置,才能开启:
config:
enabled: true -
@ConfigurationProperties
通过@ConfigurationProperties
注解可以获取一些配置在 application.properties 或 application.yml 文件中的参数信息,该注解有一个prefix属性,通过指定的前缀,绑定配置文件中的配置。示例:
spring:
redis
host: ***
port: ***
// 可以通过@ConfigurationProperties来获取配置信息(参数名要和配置的一致)
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private String host;
private String port;
}
3、引入依赖使用即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
@Component
public class RedisTest implements CommandLineRunner {
@Autowired
RedisTemplate redisTemplate;
@Override
public void run(String... args) throws Exception {
redisTemplate.opsForValue().set("test", 22);
Integer test = (Integer) redisTemplate.opsForValue().get("test");
System.out.println(test);
}
}
3、String类型常用api
-
void set(K var1, V var2):新增一个字符串类型的值,K是键,V是值:
@Component
@Slf4j
public class RedisTest implements CommandLineRunner {
@Autowired
RedisTemplate redisTemplate;
@Override
public void run(String... args) throws Exception {
redisTemplate.opsForValue().set("string_test", 22);
redisTemplate.opsForValue().set("string_test1", "abc");
Person person = new Person();
person.setName("aa");
person.setAge(18);
redisTemplate.opsForValue().set("string_test2", person);
log.info("success");
}
}
-
void set(K var1, V var2, long var3, TimeUnit var5):新增一个带过期时间的字符串类型的值,K是键,V是值,var3是过期时间,var5是过期时间单位
@Override
public void run(String... args) throws Exception {
// 60s过期
redisTemplate.opsForValue().set("string_test3", "happy", 60L, TimeUnit.SECONDS);
log.info("success");
}
-
V get(Object var1):获取key键对应的值
@Override
public void run(String... args) throws Exception {
String stringTest = (String) redisTemplate.opsForValue().get("string_test1");
if (StringUtils.isNotEmpty(stringTest)){
log.info("stringTest:{}", stringTest);
}
log.info("success");
}
-
Long increment(K var1, long var2):原子类型自增,K是键,var2是要自增的数量(如果key不存在,会创建一个key,然后默认值为0,然后再进行递增操作)
@Override
public void run(String... args) throws Exception {
Long stringTest = redisTemplate.opsForValue().increment("string_test", 1);
if (Objects.nonNull(stringTest)){
log.info("自增之后的值:{}", stringTest);
}
log.info("success");
}
-
Long decrement(K var1, long var2):原子类型自增,K是键,var2是要自减的数量(如果key不存在,会创建一个key,然后默认值为0,然后再进行递减操作)
@Override
public void run(String... args) throws Exception {
Long stringTest = redisTemplate.opsForValue().decrement("string_test", 1);
if (Objects.nonNull(stringTest)){
log.info("自增减之后的值:{}", stringTest);
}
log.info("success");
}
-
Boolean setIfAbsent(K var1, V var2):如果键不存在则新增,存在则不改变已经有的值(跟上面set方法一样,也有带过期时间的:Boolean setIfAbsent(K var1, V var2, long var3, TimeUnit var5))
@Override
public void run(String... args) throws Exception {
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("string_test1", "def");
log.info("setIfAbsent:{}", aBoolean);
String stringTest = (String) redisTemplate.opsForValue().get("string_test1");
if (Objects.nonNull(stringTest)){
log.info("stringTest:{}", stringTest);
}
log.info("success");
}
-
void multiSet(Map<? extends K, ? extends V> var1):批量设置同一类型的值
@Override
public void run(String... args) throws Exception {
Map<String, String> map = new HashMap<>(4);
map.put("map_test1", "我是谁");
map.put("map_test2", "我是帅哥");
redisTemplate.opsForValue().multiSet(map);
log.info("success");
}
-
List<V> multiGet(Collection<K> var1):批量获取同一类型的值
@Override
public void run(String... args) throws Exception {
List<String> stringList = new ArrayList<>();
stringList.add("map_test1");
stringList.add("map_test2");
List<String> list = redisTemplate.opsForValue().multiGet(stringList);
list.forEach(s -> {
log.info("值:{}", s);
});
log.info("success");
}
-
Boolean multiSetIfAbsent(Map<? extends K, ? extends V> var1):批量设置,如果其中有任意一个key存在,则整体都不设置,返回false
@Override
public void run(String... args) throws Exception {
Map<String, String> map = new HashMap<>(4);
map.put("map_test1", "我是谁");
map.put("map_test3", "我是一个帅哥");
Boolean aBoolean = redisTemplate.opsForValue().multiSetIfAbsent(map);
log.info("aBoolean:{}", aBoolean);
log.info("success");
}
@Override
public void run(String... args) throws Exception {
Map<String, String> map = new HashMap<>(4);
map.put("map_test3", "我是一个帅哥");
map.put("map_test4", "我真的真的是个帅哥");
Boolean aBoolean = redisTemplate.opsForValue().multiSetIfAbsent(map);
log.info("aBoolean:{}", aBoolean);
log.info("success");
}
-
V getAndSet(K var1, V var2):返回旧的值并设置新的值
@Override
public void run(String... args) throws Exception {
String string = (String) redisTemplate.opsForValue().getAndSet("map_test1", "happy_new_year");
log.info("旧值:{}", string);
String newString = (String) redisTemplate.opsForValue().get("map_test1");
log.info("新值:{}", newString);
log.info("success");
}
-
Boolean delete(K key):删除缓存key
@Override
public void run(String... args) throws Exception {
Boolean delete = redisTemplate.delete("map_test1");
log.info("是否删除:{}", delete);
log.info("success");
}
4、Hash类型常用api
-
void put(H var1, HK var2, HV var3):设置hash数据,H是大的key,HK是hash小key,HV是值
@Override
public void run(String... args) throws Exception {
redisTemplate.opsForHash().put("big_map", "small_map", "我是值");
log.info("success");
}
-
Boolean putIfAbsent(H var1, HK var2, HV var3):如果HK不存在则增加,存在则返回false(大key可以一致,但是小key不能一致)
@Override
public void run(String... args) throws Exception {
Boolean aBoolean = redisTemplate.opsForHash().putIfAbsent("big_map", "small_map1", "我是值");
log.info("是否增加:{}", aBoolean);
log.info("success");
}
-
HV get(H var1, Object var2):根据大key和小key获取值
@Override
public void run(String... args) throws Exception {
String s = (String) redisTemplate.opsForHash().get("big_map", "small_map1");
log.info("获取的值:{}", s);
log.info("success");
}
-
Long delete(H var1, Object... var2):根据大key和小key删除数据(可以传入多个参数,删除多个键值对)
@Override
public void run(String... args) throws Exception {
Long delete = redisTemplate.opsForHash().delete("big_map", "small_map");
log.info("删除数量:{}", delete);
log.info("success");
}
@Override
public void run(String... args) throws Exception {
Long delete = redisTemplate.opsForHash().delete("big_map", "small_map", "small_map1");
log.info("删除数量:{}", delete);
log.info("success");
}
-
Long size(H var1):查询map的size大小,参数为大key
@Override
public void run(String... args) throws Exception {
Long size = redisTemplate.opsForHash().size("big_map");
log.info("数量:{}", size);
log.info("success");
}
-
Long increment(H var1, HK var2, long var3):原子自增(多线程情况下数值要自增的话尽量使用这个方法)
@Override
public void run(String... args) throws Exception {
// 如果key不存在,则会新建,值为0
Long increment = redisTemplate.opsForHash().increment("map_test", "test", 1);
log.info("自增之后的值:{}", increment);
log.info("success");
}
-
Long lengthOfValue(H var1, HK var2):查询value的长度
@Override
public void run(String... args) throws Exception {
Long length = redisTemplate.opsForHash().lengthOfValue("map_test", "test");
log.info("值长度:{}", length);
log.info("success");
}
-
Map<HK, HV> entries(H var1):获取键值对,参数为大key
@Override
public void run(String... args) throws Exception {
Map<String, String> bigMap = redisTemplate.opsForHash().entries("big_map");
Iterator<Map.Entry<String, String>> iterator = bigMap.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<String, String> stringEntry = iterator.next();
log.info("key:{}, 值:{}", stringEntry.getKey(), stringEntry.getValue());
}
log.info("success");
}
-
Boolean hasKey(H var1, Object var2):判断是否存在指定的map键
@Override
public void run(String... args) throws Exception {
Boolean aBoolean = redisTemplate.opsForHash().hasKey("map_test", "test");
log.info("是否存在:{}", aBoolean);
log.info("success");
}
-
Set<HK> keys(H var1):获取大key中的所有小key,参数为大key
@Override
public void run(String... args) throws Exception {
Set<String> mapTest = redisTemplate.opsForHash().keys("map_test");
mapTest.forEach(s -> {
log.info("小key:{}", s);
});
log.info("success");
}
-
List<HV> values(H var1):获取大key中的所有小key的值,参数为大key
@Override
public void run(String... args) throws Exception {
List<Integer> mapTest = redisTemplate.opsForHash().values("map_test");
mapTest.forEach(s -> {
log.info("值:{}", s);
});
log.info("success");
}
-
List<HV> multiGet(H var1, Collection<HK> var2):批量获取,第一个参数为大key,第二个参数为小key的集合
@Override
public void run(String... args) throws Exception {
List<String> list = new ArrayList<>();
list.add("small_map3");
list.add("small_map4");
List<String> bigMap = redisTemplate.opsForHash().multiGet("big_map", list);
bigMap.forEach(s -> {
log.info("value:{}", s);
});
log.info("success");
}
-
Boolean delete(K key):删除缓存key,Hash类型的话,参数只能为大key(想删除小key可以看上面,上面有写删除小key的api)
@Override
public void run(String... args) throws Exception {
Boolean delete = redisTemplate.delete("big_map");
log.info("是否删除:{}", delete);
log.info("success");
}
5、结尾
其他操作类型的api请看下文!
如果你觉得我的文章对你有帮助话,欢迎关注我的微信公众号:"一个快乐又痛苦的程序员"(无广告,单纯分享原创文章、已pj的实用工具、各种Java学习资源,期待与你共同进步)